diff --git a/.github/workflows/links-check.yml b/.github/workflows/links-check.yml
new file mode 100644
index 0000000000..5c9fd00b76
--- /dev/null
+++ b/.github/workflows/links-check.yml
@@ -0,0 +1,21 @@
+name: Link Checker
+
+on:
+ repository_dispatch:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ linkChecker:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Link Checker
+ uses: lycheeverse/lychee-action@v1.8.0
+ with:
+ fail: true
+ args: --exclude-mail --exclude .+localhost.+ README.md
+ env:
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff --git a/.github_changelog_generator b/.github_changelog_generator
new file mode 100644
index 0000000000..375b661f50
--- /dev/null
+++ b/.github_changelog_generator
@@ -0,0 +1,3 @@
+user=aklivity
+project=zilla
+enhancement-labels=enhancement,story
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44b2003ff4..ffaae0a618 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,59 @@
## [Unreleased](https://github.com/aklivity/zilla/tree/HEAD)
-[Full Changelog](https://github.com/aklivity/zilla/compare/0.9.91...HEAD)
+[Full Changelog](https://github.com/aklivity/zilla/compare/0.9.92...HEAD)
+
+**Implemented enhancements:**
+
+- Support `pgsql` binding [\#1057](https://github.com/aklivity/zilla/issues/1057)
+- Handle large HTTP headers [\#1046](https://github.com/aklivity/zilla/issues/1046)
+- Gracefully handle `zilla.yml` instead of `zilla.yaml` [\#580](https://github.com/aklivity/zilla/issues/580)
+
+**Fixed bugs:**
+
+- Zilla OpenAPI not supporting filesystem catalog [\#1225](https://github.com/aklivity/zilla/issues/1225)
+- Zilla produces corrupt messages due to incorrect CRC when fragmented [\#1221](https://github.com/aklivity/zilla/issues/1221)
+- Issue referencing `guard` in `asyncapi` binding [\#1215](https://github.com/aklivity/zilla/issues/1215)
+- Using catalog::apicurio triggers unexpected behaviour [\#1202](https://github.com/aklivity/zilla/issues/1202)
+- `400 Bad Request` response from Zilla when Using Java HttpClient [\#1192](https://github.com/aklivity/zilla/issues/1192)
+- Investigate connection pool reconnect when Kafka not yet available [\#1153](https://github.com/aklivity/zilla/issues/1153)
+
+**Merged pull requests:**
+
+- Refactor vault handler [\#1236](https://github.com/aklivity/zilla/pull/1236) ([jfallows](https://github.com/jfallows))
+- Mqtt flow control fix [\#1233](https://github.com/aklivity/zilla/pull/1233) ([bmaidics](https://github.com/bmaidics))
+- Fix incorrect flush acknowledgement in KafkaCacheClientProduceFactory [\#1232](https://github.com/aklivity/zilla/pull/1232) ([bmaidics](https://github.com/bmaidics))
+- http binding update to support header overrides at route level [\#1231](https://github.com/aklivity/zilla/pull/1231) ([ankitk-me](https://github.com/ankitk-me))
+- Update asyncapi binding module-info to open parser package [\#1227](https://github.com/aklivity/zilla/pull/1227) ([jfallows](https://github.com/jfallows))
+- Link checker [\#1216](https://github.com/aklivity/zilla/pull/1216) ([vordimous](https://github.com/vordimous))
+- Fix incorrect CRC combine in Kafka produce client [\#1214](https://github.com/aklivity/zilla/pull/1214) ([bmaidics](https://github.com/bmaidics))
+- Reduce compile warnings [\#1213](https://github.com/aklivity/zilla/pull/1213) ([jfallows](https://github.com/jfallows))
+- Eclipse IDE import maven projects [\#1212](https://github.com/aklivity/zilla/pull/1212) ([jfallows](https://github.com/jfallows))
+- Disable JVM class sharing to avoid error message during build [\#1210](https://github.com/aklivity/zilla/pull/1210) ([jfallows](https://github.com/jfallows))
+- Initial risingwave binding projects [\#1209](https://github.com/aklivity/zilla/pull/1209) ([jfallows](https://github.com/jfallows))
+- Ensure id encoding is consistent for encode and decode [\#1204](https://github.com/aklivity/zilla/pull/1204) ([jfallows](https://github.com/jfallows))
+- Update mqtt session stream to report correct origin id for zilla dump command [\#1203](https://github.com/aklivity/zilla/pull/1203) ([jfallows](https://github.com/jfallows))
+- Support pgsql binding [\#1200](https://github.com/aklivity/zilla/pull/1200) ([akrambek](https://github.com/akrambek))
+- Initial pgsql binding projects [\#1198](https://github.com/aklivity/zilla/pull/1198) ([jfallows](https://github.com/jfallows))
+- Fix: Using `asyncapi client` binding trigger NPE & crashes Zilla [\#1197](https://github.com/aklivity/zilla/pull/1197) ([ankitk-me](https://github.com/ankitk-me))
+- Compute kafka produce checksum without staging headers [\#1196](https://github.com/aklivity/zilla/pull/1196) ([jfallows](https://github.com/jfallows))
+- Allow content-length header with h2c upgrade [\#1194](https://github.com/aklivity/zilla/pull/1194) ([jfallows](https://github.com/jfallows))
+- Kafka cache client: mark entry dirty at flush before notifying the server to process [\#1193](https://github.com/aklivity/zilla/pull/1193) ([bmaidics](https://github.com/bmaidics))
+- Ensure streams are cleaned up on authentication failure… [\#1191](https://github.com/aklivity/zilla/pull/1191) ([jfallows](https://github.com/jfallows))
+
+## [0.9.92](https://github.com/aklivity/zilla/tree/0.9.92) (2024-08-13)
+
+[Full Changelog](https://github.com/aklivity/zilla/compare/0.9.91...0.9.92)
**Implemented enhancements:**
- Support `extract-key` kafka message transform [\#1176](https://github.com/aklivity/zilla/issues/1176)
+**Merged pull requests:**
+
+- Align subject names when using inline catalog [\#1190](https://github.com/aklivity/zilla/pull/1190) ([jfallows](https://github.com/jfallows))
+- Support extract-key kafka message transform [\#1183](https://github.com/aklivity/zilla/pull/1183) ([akrambek](https://github.com/akrambek))
+
## [0.9.91](https://github.com/aklivity/zilla/tree/0.9.91) (2024-08-10)
[Full Changelog](https://github.com/aklivity/zilla/compare/0.9.90...0.9.91)
diff --git a/README.md b/README.md
index 06949a3e3f..655159ca4a 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ Use **Zilla** as a:
| [Async workflows for sync microservices →](https://github.com/aklivity/zilla-examples/tree/main/http.kafka.async) | Make request-response microservice communication asynchronous by routing it over a pair of Kafka topics. |
| [Create an event-mesh →](https://www.aklivity.io/post/end-to-end-streaming-between-grpc-services-via-kafka) | Integrate mesh and event-driven microservices by routing connectivity through Kafka. Make Kafka look like a gRPC/REST server or gRPC client. |
| [Secure a Server Sent Event (SSE) API →](https://github.com/aklivity/zilla-examples/tree/main/sse.proxy.jwt) | Secure an SSE API by adding JWT-based Continous Authorization. |
-| [Validate MQTT via AsyncAPI →](https://github.com/aklivity/zilla-examples/tree/main/mqtt.proxy.asyncapi) | Enforce an AsyncAPI schema for messages going into an MQTT broker. |
+| [Validate MQTT via AsyncAPI →](https://github.com/aklivity/zilla-examples/tree/main/asyncapi.mqtt.kafka.proxy) | Enforce an AsyncAPI schema for messages going into an MQTT broker. |
| **Much more!** | Check out all the [Zilla Demos](https://github.com/aklivity/zilla-demos) and [Zilla Examples](https://github.com/aklivity/zilla-examples). |
## Get started in 60 seconds
diff --git a/build/flyweight-maven-plugin/pom.xml b/build/flyweight-maven-plugin/pom.xml
index 37c1bcad88..b3f52643cf 100644
--- a/build/flyweight-maven-plugin/pom.xml
+++ b/build/flyweight-maven-plugin/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
build
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/build/flyweight-maven-plugin/src/main/java/io/aklivity/zilla/build/maven/plugins/flyweight/internal/AbstractMojo.java b/build/flyweight-maven-plugin/src/main/java/io/aklivity/zilla/build/maven/plugins/flyweight/internal/AbstractMojo.java
index f7e3607766..980b19d404 100644
--- a/build/flyweight-maven-plugin/src/main/java/io/aklivity/zilla/build/maven/plugins/flyweight/internal/AbstractMojo.java
+++ b/build/flyweight-maven-plugin/src/main/java/io/aklivity/zilla/build/maven/plugins/flyweight/internal/AbstractMojo.java
@@ -20,6 +20,8 @@
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.LinkedList;
import java.util.List;
@@ -65,11 +67,15 @@ ClassLoader createLoader() throws IOException
try
{
- for (Object resourcePathEntry : project.getTestClasspathElements())
+ for (String classPathElement : project.getTestClasspathElements())
{
- File resourcePathFile = new File(resourcePathEntry.toString());
- URI resourcePathURI = resourcePathFile.getAbsoluteFile().toURI();
- resourcePath.add(URI.create(String.format("jar:%s!/META-INF/zilla/", resourcePathURI)).toURL());
+ Path classPathEntry = Path.of(classPathElement).toAbsolutePath();
+ URI classPathEntryURI = classPathEntry.toUri();
+ URI resourcePathEntryURI = Files.isDirectory(classPathEntry)
+ ? classPathEntry.resolve("META-INF/zilla").toUri()
+ : URI.create(String.format("jar:%s!/META-INF/zilla/", classPathEntryURI));
+ URL resourcePathEntry = resourcePathEntryURI.toURL();
+ resourcePath.add(resourcePathEntry);
}
}
catch (DependencyResolutionRequiredException e)
diff --git a/build/pom.xml b/build/pom.xml
index 2fcb47c3ef..2f3b48bc3c 100644
--- a/build/pom.xml
+++ b/build/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/cloud/docker-image/pom.xml b/cloud/docker-image/pom.xml
index b2d916c4d7..d57f470607 100644
--- a/cloud/docker-image/pom.xml
+++ b/cloud/docker-image/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
cloud
- 0.9.92
+ 0.9.93
../pom.xml
@@ -51,37 +51,37 @@
${project.groupId}
- binding-openapi
+ binding-echo
${project.version}
runtime
${project.groupId}
- binding-openapi-asyncapi
+ binding-fan
${project.version}
runtime
${project.groupId}
- binding-echo
+ binding-filesystem
${project.version}
runtime
${project.groupId}
- binding-fan
+ binding-http
${project.version}
runtime
${project.groupId}
- binding-filesystem
+ binding-http-filesystem
${project.version}
runtime
${project.groupId}
- binding-http
+ binding-http-kafka
${project.version}
runtime
@@ -91,6 +91,12 @@
${project.version}
runtime
+
+ ${project.groupId}
+ binding-grpc-kafka
+ ${project.version}
+ runtime
+
${project.groupId}
binding-kafka
@@ -99,7 +105,7 @@
${project.groupId}
- binding-proxy
+ binding-kafka-grpc
${project.version}
runtime
@@ -111,61 +117,67 @@
${project.groupId}
- binding-sse
+ binding-mqtt-kafka
${project.version}
runtime
${project.groupId}
- binding-tcp
+ binding-openapi
${project.version}
runtime
${project.groupId}
- binding-tls
+ binding-openapi-asyncapi
${project.version}
runtime
${project.groupId}
- binding-ws
+ binding-pgsql
${project.version}
runtime
${project.groupId}
- binding-http-filesystem
+ binding-proxy
${project.version}
runtime
${project.groupId}
- binding-http-kafka
+ binding-risingwave
${project.version}
runtime
${project.groupId}
- binding-grpc-kafka
+ binding-sse
${project.version}
runtime
${project.groupId}
- binding-mqtt-kafka
+ binding-sse-kafka
${project.version}
runtime
${project.groupId}
- binding-sse-kafka
+ binding-tcp
${project.version}
runtime
${project.groupId}
- binding-kafka-grpc
+ binding-tls
+ ${project.version}
+ runtime
+
+
+ ${project.groupId}
+ binding-ws
${project.version}
runtime
@@ -376,7 +388,7 @@
io.fabric8
docker-maven-plugin
- 0.43.2
+ 0.45.0
${*}
diff --git a/cloud/docker-image/src/main/docker/zpm.json.template b/cloud/docker-image/src/main/docker/zpm.json.template
index 7ae3d0f995..d3a9925787 100644
--- a/cloud/docker-image/src/main/docker/zpm.json.template
+++ b/cloud/docker-image/src/main/docker/zpm.json.template
@@ -29,7 +29,9 @@
"io.aklivity.zilla:binding-mqtt-kafka",
"io.aklivity.zilla:binding-openapi",
"io.aklivity.zilla:binding-openapi-asyncapi",
+ "io.aklivity.zilla:binding-pgsql",
"io.aklivity.zilla:binding-proxy",
+ "io.aklivity.zilla:binding-risingwave",
"io.aklivity.zilla:binding-sse",
"io.aklivity.zilla:binding-sse-kafka",
"io.aklivity.zilla:binding-tcp",
diff --git a/cloud/helm-chart/pom.xml b/cloud/helm-chart/pom.xml
index 67cc6bdc30..81f31da6c1 100644
--- a/cloud/helm-chart/pom.xml
+++ b/cloud/helm-chart/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
cloud
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/cloud/pom.xml b/cloud/pom.xml
index a60f2f3b38..d851548d86 100644
--- a/cloud/pom.xml
+++ b/cloud/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/conf/pom.xml b/conf/pom.xml
index 65fcb9616d..0b83423bd6 100644
--- a/conf/pom.xml
+++ b/conf/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/configuration.xml b/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/configuration.xml
index d710133e57..1224d5fd77 100644
--- a/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/configuration.xml
+++ b/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/configuration.xml
@@ -18,6 +18,10 @@
+
+
+
+
diff --git a/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/suppressions.xml b/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/suppressions.xml
index 0dd8c7aab1..2e54379c17 100644
--- a/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/suppressions.xml
+++ b/conf/src/main/resources/io/aklivity/zilla/conf/checkstyle/suppressions.xml
@@ -21,6 +21,4 @@
-
-
diff --git a/incubator/binding-amqp.spec/pom.xml b/incubator/binding-amqp.spec/pom.xml
index 39c74a14d6..e1b78131aa 100644
--- a/incubator/binding-amqp.spec/pom.xml
+++ b/incubator/binding-amqp.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
incubator
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/incubator/binding-amqp/pom.xml b/incubator/binding-amqp/pom.xml
index ea64884fef..02881fd767 100644
--- a/incubator/binding-amqp/pom.xml
+++ b/incubator/binding-amqp/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
incubator
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/incubator/binding-amqp/src/main/java/io/aklivity/zilla/runtime/binding/amqp/internal/stream/AmqpServerFactory.java b/incubator/binding-amqp/src/main/java/io/aklivity/zilla/runtime/binding/amqp/internal/stream/AmqpServerFactory.java
index c26de20cad..3797788cb6 100644
--- a/incubator/binding-amqp/src/main/java/io/aklivity/zilla/runtime/binding/amqp/internal/stream/AmqpServerFactory.java
+++ b/incubator/binding-amqp/src/main/java/io/aklivity/zilla/runtime/binding/amqp/internal/stream/AmqpServerFactory.java
@@ -254,8 +254,6 @@ public final class AmqpServerFactory implements AmqpStreamFactory
private final AmqpMapFW annotationsRO = new AmqpMapFW<>(new AmqpValueFW(), new AmqpValueFW());
private final OctetsFW deliveryTagRO = new OctetsFW();
private final AmqpMessagePropertiesFW amqpPropertiesRO = new AmqpMessagePropertiesFW();
- private final AmqpMapFW applicationPropertyRO =
- new AmqpMapFW<>(new AmqpValueFW(), new AmqpValueFW());
private final AmqpApplicationPropertiesFW applicationPropertiesRO =
new AmqpApplicationPropertiesFW<>(new AmqpStringFW(), new AmqpSimpleTypeFW());
private final AmqpMapFW footerRO = new AmqpMapFW<>(new AmqpValueFW(), new AmqpValueFW());
@@ -275,7 +273,6 @@ public final class AmqpServerFactory implements AmqpStreamFactory
private final AmqpCloseFW.Builder amqpCloseRW = new AmqpCloseFW.Builder();
private final AmqpErrorListFW.Builder amqpErrorListRW = new AmqpErrorListFW.Builder();
private final AmqpStringFW.Builder amqpStringRW = new AmqpStringFW.Builder();
- private final AmqpSimpleTypeFW.Builder amqpValueRW = new AmqpSimpleTypeFW.Builder();
private final AmqpSymbolFW.Builder amqpSymbolRW = new AmqpSymbolFW.Builder();
private final AmqpSourceListFW.Builder amqpSourceListRW = new AmqpSourceListFW.Builder();
private final AmqpTargetListFW.Builder amqpTargetListRW = new AmqpTargetListFW.Builder();
@@ -4503,6 +4500,8 @@ private int encodeSectionData(
messageFragmentRW.put(buffer, progress, Integer.BYTES);
progress += Integer.BYTES;
break;
+ default:
+ break;
}
this.sectionEncoder = this::encodeSectionDataBytes;
}
@@ -4553,6 +4552,8 @@ private int encodeSectionSequence(
messageFragmentRW.put(buffer, progress, Integer.BYTES);
progress += Integer.BYTES;
break;
+ default:
+ break;
}
this.sectionEncoder = this::encodeSectionSequenceBytes;
}
diff --git a/incubator/binding-pgsql.spec/COPYRIGHT b/incubator/binding-pgsql.spec/COPYRIGHT
new file mode 100644
index 0000000000..0cb10b6f62
--- /dev/null
+++ b/incubator/binding-pgsql.spec/COPYRIGHT
@@ -0,0 +1,12 @@
+Copyright ${copyrightYears} Aklivity Inc
+
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
diff --git a/incubator/binding-pgsql.spec/LICENSE b/incubator/binding-pgsql.spec/LICENSE
new file mode 100644
index 0000000000..f3cf11c3ad
--- /dev/null
+++ b/incubator/binding-pgsql.spec/LICENSE
@@ -0,0 +1,114 @@
+ Aklivity Community License Agreement
+ Version 1.0
+
+This Aklivity Community License Agreement Version 1.0 (the “Agreement”) sets
+forth the terms on which Aklivity, Inc. (“Aklivity”) makes available certain
+software made available by Aklivity under this Agreement (the “Software”). BY
+INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF THE SOFTWARE,
+YOU AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO
+SUCH TERMS AND CONDITIONS, YOU MUST NOT USE THE SOFTWARE. IF YOU ARE RECEIVING
+THE SOFTWARE ON BEHALF OF A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU
+HAVE THE ACTUAL AUTHORITY TO AGREE TO THE TERMS AND CONDITIONS OF THIS
+AGREEMENT ON BEHALF OF SUCH ENTITY. “Licensee” means you, an individual, or
+the entity on whose behalf you are receiving the Software.
+
+ 1. LICENSE GRANT AND CONDITIONS.
+
+ 1.1 License. Subject to the terms and conditions of this Agreement,
+ Aklivity hereby grants to Licensee a non-exclusive, royalty-free,
+ worldwide, non-transferable, non-sublicenseable license during the term
+ of this Agreement to: (a) use the Software; (b) prepare modifications and
+ derivative works of the Software; (c) distribute the Software (including
+ without limitation in source code or object code form); and (d) reproduce
+ copies of the Software (the “License”). Licensee is not granted the
+ right to, and Licensee shall not, exercise the License for an Excluded
+ Purpose. For purposes of this Agreement, “Excluded Purpose” means making
+ available any software-as-a-service, platform-as-a-service,
+ infrastructure-as-a-service or other similar online service that competes
+ with Aklivity products or services that provide the Software.
+
+ 1.2 Conditions. In consideration of the License, Licensee’s distribution
+ of the Software is subject to the following conditions:
+
+ (a) Licensee must cause any Software modified by Licensee to carry
+ prominent notices stating that Licensee modified the Software.
+
+ (b) On each Software copy, Licensee shall reproduce and not remove or
+ alter all Aklivity or third party copyright or other proprietary
+ notices contained in the Software, and Licensee must provide the
+ notice below with each copy.
+
+ “This software is made available by Aklivity, Inc., under the
+ terms of the Aklivity Community License Agreement, Version 1.0
+ located at http://www.Aklivity.io/Aklivity-community-license. BY
+ INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF
+ THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT.”
+
+ 1.3 Licensee Modifications. Licensee may add its own copyright notices
+ to modifications made by Licensee and may provide additional or different
+ license terms and conditions for use, reproduction, or distribution of
+ Licensee’s modifications. While redistributing the Software or
+ modifications thereof, Licensee may choose to offer, for a fee or free of
+ charge, support, warranty, indemnity, or other obligations. Licensee, and
+ not Aklivity, will be responsible for any such obligations.
+
+ 1.4 No Sublicensing. The License does not include the right to
+ sublicense the Software, however, each recipient to which Licensee
+ provides the Software may exercise the Licenses so long as such recipient
+ agrees to the terms and conditions of this Agreement.
+
+ 2. TERM AND TERMINATION. This Agreement will continue unless and until
+ earlier terminated as set forth herein. If Licensee breaches any of its
+ conditions or obligations under this Agreement, this Agreement will
+ terminate automatically and the License will terminate automatically and
+ permanently.
+
+ 3. INTELLECTUAL PROPERTY. As between the parties, Aklivity will retain all
+ right, title, and interest in the Software, and all intellectual property
+ rights therein. Aklivity hereby reserves all rights not expressly granted
+ to Licensee in this Agreement. Aklivity hereby reserves all rights in its
+ trademarks and service marks, and no licenses therein are granted in this
+ Agreement.
+
+ 4. DISCLAIMER. Aklivity HEREBY DISCLAIMS ANY AND ALL WARRANTIES AND
+ CONDITIONS, EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, AND SPECIFICALLY
+ DISCLAIMS ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE, WITH RESPECT TO THE SOFTWARE.
+
+ 5. LIMITATION OF LIABILITY. Aklivity WILL NOT BE LIABLE FOR ANY DAMAGES OF
+ ANY KIND, INCLUDING BUT NOT LIMITED TO, LOST PROFITS OR ANY CONSEQUENTIAL,
+ SPECIAL, INCIDENTAL, INDIRECT, OR DIRECT DAMAGES, HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, ARISING OUT OF THIS AGREEMENT. THE FOREGOING SHALL
+ APPLY TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+
+ 6.GENERAL.
+
+ 6.1 Governing Law. This Agreement will be governed by and interpreted in
+ accordance with the laws of the state of California, without reference to
+ its conflict of laws principles. If Licensee is located within the
+ United States, all disputes arising out of this Agreement are subject to
+ the exclusive jurisdiction of courts located in Santa Clara County,
+ California. USA. If Licensee is located outside of the United States,
+ any dispute, controversy or claim arising out of or relating to this
+ Agreement will be referred to and finally determined by arbitration in
+ accordance with the JAMS International Arbitration Rules. The tribunal
+ will consist of one arbitrator. The place of arbitration will be Palo
+ Alto, California. The language to be used in the arbitral proceedings
+ will be English. Judgment upon the award rendered by the arbitrator may
+ be entered in any court having jurisdiction thereof.
+
+ 6.2 Assignment. Licensee is not authorized to assign its rights under
+ this Agreement to any third party. Aklivity may freely assign its rights
+ under this Agreement to any third party.
+
+ 6.3 Other. This Agreement is the entire agreement between the parties
+ regarding the subject matter hereof. No amendment or modification of
+ this Agreement will be valid or binding upon the parties unless made in
+ writing and signed by the duly authorized representatives of both
+ parties. In the event that any provision, including without limitation
+ any condition, of this Agreement is held to be unenforceable, this
+ Agreement and all licenses and rights granted hereunder will immediately
+ terminate. Waiver by Aklivity of a breach of any provision of this
+ Agreement or the failure by Aklivity to exercise any right hereunder
+ will not be construed as a waiver of any subsequent breach of that right
+ or as a waiver of any other right.
\ No newline at end of file
diff --git a/incubator/binding-pgsql.spec/NOTICE b/incubator/binding-pgsql.spec/NOTICE
new file mode 100644
index 0000000000..cf99769e41
--- /dev/null
+++ b/incubator/binding-pgsql.spec/NOTICE
@@ -0,0 +1,18 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+ agrona under The Apache License, Version 2.0
+ ICU4J under Unicode/ICU License
+ Jakarta JSON Processing API under Eclipse Public License 2.0 or GNU General Public License, version 2 with the GNU Classpath Exception
+ org.leadpony.justify under The Apache Software License, Version 2.0
+ zilla::specs::engine.spec under The Apache Software License, Version 2.0
+
diff --git a/incubator/binding-pgsql.spec/NOTICE.template b/incubator/binding-pgsql.spec/NOTICE.template
new file mode 100644
index 0000000000..209ca12f74
--- /dev/null
+++ b/incubator/binding-pgsql.spec/NOTICE.template
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+#GENERATED_NOTICES#
diff --git a/incubator/binding-pgsql.spec/mvnw b/incubator/binding-pgsql.spec/mvnw
new file mode 100755
index 0000000000..d2f0ea3808
--- /dev/null
+++ b/incubator/binding-pgsql.spec/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/incubator/binding-pgsql.spec/mvnw.cmd b/incubator/binding-pgsql.spec/mvnw.cmd
new file mode 100644
index 0000000000..b26ab24f03
--- /dev/null
+++ b/incubator/binding-pgsql.spec/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/incubator/binding-pgsql.spec/pom.xml b/incubator/binding-pgsql.spec/pom.xml
new file mode 100644
index 0000000000..36198495c7
--- /dev/null
+++ b/incubator/binding-pgsql.spec/pom.xml
@@ -0,0 +1,159 @@
+
+
+
+ 4.0.0
+
+ io.aklivity.zilla
+ incubator
+ 0.9.93
+ ../pom.xml
+
+
+ binding-pgsql.spec
+ zilla::incubator::binding-pgsql.spec
+
+
+
+ Aklivity Community License Agreement
+ https://www.aklivity.io/aklivity-community-license/
+ repo
+
+
+
+
+ 1.00
+ 0
+
+
+
+
+ io.aklivity.k3po
+ lang
+ provided
+
+
+ ${project.groupId}
+ engine.spec
+ ${project.version}
+
+
+ junit
+ junit
+ test
+
+
+ io.aklivity.k3po
+ control-junit
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+
+
+
+
+ src/main/resources
+
+
+ src/main/scripts
+
+
+
+
+
+ org.jasig.maven
+ maven-notice-plugin
+
+
+ ${project.groupId}
+ flyweight-maven-plugin
+ ${project.version}
+
+ core pgsql
+ io.aklivity.zilla.specs.binding.pgsql.internal.types
+
+
+
+
+ validate
+ generate
+
+
+
+
+
+ com.mycila
+ license-maven-plugin
+
+
+ maven-checkstyle-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ io.aklivity.k3po
+ k3po-maven-plugin
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ test-jar
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ io/aklivity/zilla/specs/binding/pgsql/internal/types/**/*.class
+
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ ${jacoco.coverage.ratio}
+
+
+ CLASS
+ MISSEDCOUNT
+ ${jacoco.missed.count}
+
+
+
+
+
+
+
+
+
diff --git a/incubator/binding-pgsql.spec/src/main/java/io/aklivity/zilla/specs/binding/pgsql/PgsqlFunctions.java b/incubator/binding-pgsql.spec/src/main/java/io/aklivity/zilla/specs/binding/pgsql/PgsqlFunctions.java
new file mode 100644
index 0000000000..ca46aa48c1
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/java/io/aklivity/zilla/specs/binding/pgsql/PgsqlFunctions.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.specs.binding.pgsql;
+
+import org.agrona.MutableDirectBuffer;
+import org.agrona.concurrent.UnsafeBuffer;
+
+import io.aklivity.k3po.runtime.lang.el.Function;
+import io.aklivity.k3po.runtime.lang.el.spi.FunctionMapperSpi;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlBeginExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlColumnInfoFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlCompletedFlushExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlDataExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlFlushExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlFormat;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlMessageType;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlQueryDataExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlReadyFlushExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlRowDataExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlStatus;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlTypeFlushExFW;
+
+public final class PgsqlFunctions
+{
+ @Function
+ public static PgsqlBeginExBuilder beginEx()
+ {
+ return new PgsqlBeginExBuilder();
+ }
+
+ @Function
+ public static PgsqlDataExBuilder dataEx()
+ {
+ return new PgsqlDataExBuilder();
+ }
+
+ @Function
+ public static PgsqlFlushExBuilder flushEx()
+ {
+ return new PgsqlFlushExBuilder();
+ }
+
+ public static final class PgsqlBeginExBuilder
+ {
+ private final MutableDirectBuffer writeBuffer = new UnsafeBuffer(new byte[1024 * 8]);
+
+ private final PgsqlBeginExFW.Builder beginExRW = new PgsqlBeginExFW.Builder();
+
+ private PgsqlBeginExBuilder()
+ {
+ beginExRW.wrap(writeBuffer, 0, writeBuffer.capacity());
+ }
+
+ public PgsqlBeginExBuilder typeId(
+ int typeId)
+ {
+ beginExRW.typeId(typeId);
+ return this;
+ }
+
+ public PgsqlBeginExBuilder parameter(
+ String name,
+ String value)
+ {
+ beginExRW.parametersItem(p -> p
+ .name(String.format("%s\u0000", name))
+ .value(String.format("%s\u0000", value)));
+
+ return this;
+ }
+
+ public byte[] build()
+ {
+ final PgsqlBeginExFW beginEx = beginExRW.build();
+ final byte[] array = new byte[beginEx.sizeof()];
+ beginEx.buffer().getBytes(beginEx.offset(), array);
+ return array;
+ }
+ }
+
+ public static final class PgsqlDataExBuilder
+ {
+ private final MutableDirectBuffer writeBuffer = new UnsafeBuffer(new byte[1024 * 8]);
+
+ private final PgsqlDataExFW dataExRO = new PgsqlDataExFW();
+
+ private final PgsqlDataExFW.Builder dataExRW = new PgsqlDataExFW.Builder();
+
+ private PgsqlDataExBuilder()
+ {
+ dataExRW.wrap(writeBuffer, 0, writeBuffer.capacity());
+ }
+
+ public PgsqlDataExBuilder typeId(
+ int typeId)
+ {
+ dataExRW.typeId(typeId);
+ return this;
+ }
+
+ public PgsqlQueryDataExBuilder query()
+ {
+ dataExRW.kind(PgsqlMessageType.QUERY.value());
+
+ return new PgsqlQueryDataExBuilder();
+ }
+
+ public PgsqlRowDataExBuilder row()
+ {
+ dataExRW.kind(PgsqlMessageType.ROW.value());
+
+ return new PgsqlRowDataExBuilder();
+ }
+
+ public byte[] build()
+ {
+ final PgsqlDataExFW dataEx = dataExRO;
+ final byte[] array = new byte[dataEx.sizeof()];
+ dataEx.buffer().getBytes(dataEx.offset(), array);
+ return array;
+ }
+
+ public final class PgsqlQueryDataExBuilder
+ {
+ private final PgsqlQueryDataExFW.Builder pgsqlQueryDataExRW = new PgsqlQueryDataExFW.Builder();
+
+ private PgsqlQueryDataExBuilder()
+ {
+ pgsqlQueryDataExRW.wrap(writeBuffer, PgsqlDataExFW.FIELD_OFFSET_QUERY, writeBuffer.capacity());
+ }
+
+ public PgsqlQueryDataExBuilder deferred(
+ int deferred)
+ {
+ pgsqlQueryDataExRW.deferred(deferred);
+ return this;
+ }
+
+ public PgsqlDataExBuilder build()
+ {
+ final PgsqlQueryDataExFW pgsqlQueryDataEx = pgsqlQueryDataExRW.build();
+ dataExRO.wrap(writeBuffer, 0, pgsqlQueryDataEx.limit());
+ return PgsqlDataExBuilder.this;
+ }
+ }
+
+ public final class PgsqlRowDataExBuilder
+ {
+ private final PgsqlRowDataExFW.Builder pgsqlRowDataExRW = new PgsqlRowDataExFW.Builder();
+
+ private PgsqlRowDataExBuilder()
+ {
+ pgsqlRowDataExRW.wrap(writeBuffer, PgsqlDataExFW.FIELD_OFFSET_QUERY, writeBuffer.capacity());
+ }
+
+ public PgsqlRowDataExBuilder deferred(
+ int deferred)
+ {
+ pgsqlRowDataExRW.deferred(deferred);
+ return this;
+ }
+
+ public PgsqlDataExBuilder build()
+ {
+ final PgsqlRowDataExFW pgsqlRowDataEx = pgsqlRowDataExRW.build();
+ dataExRO.wrap(writeBuffer, 0, pgsqlRowDataEx.limit());
+ return PgsqlDataExBuilder.this;
+ }
+ }
+ }
+
+ public static final class PgsqlFlushExBuilder
+ {
+ private final MutableDirectBuffer writeBuffer = new UnsafeBuffer(new byte[1024 * 8]);
+
+ private final PgsqlFlushExFW flushExRO = new PgsqlFlushExFW();
+
+ private final PgsqlFlushExFW.Builder flushExRW = new PgsqlFlushExFW.Builder();
+
+ private PgsqlFlushExBuilder()
+ {
+ flushExRW.wrap(writeBuffer, 0, writeBuffer.capacity());
+ }
+
+ public PgsqlFlushExBuilder typeId(
+ int typeId)
+ {
+ flushExRW.typeId(typeId);
+ return this;
+ }
+
+ public PgsqlTypeFlushExBuilder type()
+ {
+ flushExRW.kind(PgsqlMessageType.TYPE.value());
+
+ return new PgsqlTypeFlushExBuilder();
+ }
+
+ public PgsqlCompletedFlushExBuilder completion()
+ {
+ flushExRW.kind(PgsqlMessageType.COMPLETION.value());
+
+ return new PgsqlCompletedFlushExBuilder();
+ }
+
+ public PgsqlReadyFlushExBuilder ready()
+ {
+ flushExRW.kind(PgsqlMessageType.READY.value());
+
+ return new PgsqlReadyFlushExBuilder();
+ }
+
+ public byte[] build()
+ {
+ final PgsqlFlushExFW dataEx = flushExRO;
+ final byte[] array = new byte[dataEx.sizeof()];
+ dataEx.buffer().getBytes(dataEx.offset(), array);
+ return array;
+ }
+
+ public final class PgsqlTypeFlushExBuilder
+ {
+ private final PgsqlTypeFlushExFW.Builder pgsqlTypeFlushExRW = new PgsqlTypeFlushExFW.Builder();
+
+ private PgsqlTypeFlushExBuilder()
+ {
+ pgsqlTypeFlushExRW.wrap(writeBuffer, PgsqlFlushExFW.FIELD_OFFSET_TYPE, writeBuffer.capacity());
+ }
+
+ public PgsqlColumnInfoBuilder column()
+ {
+ return new PgsqlColumnInfoBuilder();
+ }
+
+ public final class PgsqlColumnInfoBuilder
+ {
+ private final MutableDirectBuffer columnInfoBuffer = new UnsafeBuffer(new byte[1024 * 8]);
+ private final PgsqlColumnInfoFW.Builder columnInfoRW = new PgsqlColumnInfoFW.Builder();
+
+ PgsqlColumnInfoBuilder()
+ {
+ columnInfoRW.wrap(columnInfoBuffer, 0, columnInfoBuffer.capacity());
+ }
+
+ public PgsqlColumnInfoBuilder name(
+ String name)
+ {
+ columnInfoRW.name(String.format("%s\u0000", name));
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder tableOid(
+ int tableOid)
+ {
+ columnInfoRW.tableOid(tableOid);
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder index(
+ short index)
+ {
+ columnInfoRW.index(index);
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder typeOid(
+ int typeOid)
+ {
+ columnInfoRW.typeOid(typeOid);
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder length(
+ short length)
+ {
+ columnInfoRW.length(length);
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder modifier(
+ int modifier)
+ {
+ columnInfoRW.modifier(modifier);
+ return this;
+ }
+
+ public PgsqlColumnInfoBuilder format(
+ String format)
+ {
+ columnInfoRW.format(f -> f.set(PgsqlFormat.valueOf(format)));
+ return this;
+ }
+
+ public PgsqlTypeFlushExBuilder build()
+ {
+ PgsqlColumnInfoFW columnInfo = columnInfoRW.build();
+ pgsqlTypeFlushExRW.columnsItem(c -> c
+ .name(columnInfo.name())
+ .tableOid(columnInfo.tableOid())
+ .index(columnInfo.index())
+ .typeOid(columnInfo.typeOid())
+ .length(columnInfo.length())
+ .modifier(columnInfo.modifier())
+ .format(columnInfo.format()));
+
+ return PgsqlTypeFlushExBuilder.this;
+ }
+ }
+
+ public PgsqlFlushExBuilder build()
+ {
+ final PgsqlTypeFlushExFW pgsqlTypeFlushEx = pgsqlTypeFlushExRW.build();
+ flushExRO.wrap(writeBuffer, 0, pgsqlTypeFlushEx.limit());
+ return PgsqlFlushExBuilder.this;
+ }
+ }
+
+ public final class PgsqlCompletedFlushExBuilder
+ {
+ private final PgsqlCompletedFlushExFW.Builder pgsqlCompletedFlushExRW = new PgsqlCompletedFlushExFW.Builder();
+
+ private PgsqlCompletedFlushExBuilder()
+ {
+ pgsqlCompletedFlushExRW.wrap(writeBuffer, PgsqlFlushExFW.FIELD_OFFSET_TYPE, writeBuffer.capacity());
+ }
+
+ public PgsqlCompletedFlushExBuilder tag(
+ String tag)
+ {
+ pgsqlCompletedFlushExRW.tag(String.format("%s\u0000", tag));
+ return this;
+ }
+
+ public PgsqlFlushExBuilder build()
+ {
+ final PgsqlCompletedFlushExFW pgsqlCompletedFlushEx = pgsqlCompletedFlushExRW.build();
+ flushExRO.wrap(writeBuffer, 0, pgsqlCompletedFlushEx.limit());
+ return PgsqlFlushExBuilder.this;
+ }
+ }
+
+ public final class PgsqlReadyFlushExBuilder
+ {
+ private final PgsqlReadyFlushExFW.Builder pgsqlReadyFlushExRW = new PgsqlReadyFlushExFW.Builder();
+
+ private PgsqlReadyFlushExBuilder()
+ {
+ pgsqlReadyFlushExRW.wrap(writeBuffer, PgsqlFlushExFW.FIELD_OFFSET_TYPE, writeBuffer.capacity());
+ }
+
+ public PgsqlReadyFlushExBuilder status(
+ String status)
+ {
+ pgsqlReadyFlushExRW.status(s -> s.set(PgsqlStatus.valueOf(status)));
+ return this;
+ }
+
+ public PgsqlFlushExBuilder build()
+ {
+ final PgsqlReadyFlushExFW pgsqlReadyFlushEx = pgsqlReadyFlushExRW.build();
+ flushExRO.wrap(writeBuffer, 0, pgsqlReadyFlushEx.limit());
+ return PgsqlFlushExBuilder.this;
+ }
+ }
+ }
+
+ private PgsqlFunctions()
+ {
+ // utility
+ }
+
+ public static class Mapper extends FunctionMapperSpi.Reflective
+ {
+
+ public Mapper()
+ {
+ super(PgsqlFunctions.class);
+ }
+
+ @Override
+ public String getPrefixName()
+ {
+ return "pgsql";
+ }
+ }
+}
diff --git a/incubator/binding-pgsql.spec/src/main/moditect/module-info.java b/incubator/binding-pgsql.spec/src/main/moditect/module-info.java
new file mode 100644
index 0000000000..521d25ef4b
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/moditect/module-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+open module io.aklivity.zilla.specs.binding.pgsql
+{
+ requires transitive io.aklivity.zilla.specs.engine;
+}
diff --git a/incubator/binding-pgsql.spec/src/main/resources/META-INF/services/io.aklivity.k3po.runtime.lang.el.spi.FunctionMapperSpi b/incubator/binding-pgsql.spec/src/main/resources/META-INF/services/io.aklivity.k3po.runtime.lang.el.spi.FunctionMapperSpi
new file mode 100644
index 0000000000..0a11bf0815
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/resources/META-INF/services/io.aklivity.k3po.runtime.lang.el.spi.FunctionMapperSpi
@@ -0,0 +1 @@
+io.aklivity.zilla.specs.binding.pgsql.PgsqlFunctions$Mapper
diff --git a/incubator/binding-pgsql.spec/src/main/resources/META-INF/zilla/pgsql.idl b/incubator/binding-pgsql.spec/src/main/resources/META-INF/zilla/pgsql.idl
new file mode 100644
index 0000000000..8e9de96273
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/resources/META-INF/zilla/pgsql.idl
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+scope pgsql
+{
+ scope stream
+ {
+ struct PgsqlParameter
+ {
+ string16 name;
+ string16 value;
+ }
+
+ struct PgsqlBeginEx extends core::stream::Extension
+ {
+ PgsqlParameter[] parameters;
+ }
+
+ enum PgsqlMessageType (uint8)
+ {
+ QUERY (81),
+ ROW (68),
+ TYPE (84),
+ COMPLETION (67),
+ READY (90)
+ }
+
+ union PgsqlDataEx switch (uint8) extends core::stream::Extension
+ {
+ case 68: pgsql::stream::PgsqlRowDataEx row;
+ case 81: pgsql::stream::PgsqlQueryDataEx query;
+ }
+
+ struct PgsqlQueryDataEx
+ {
+ int32 deferred = 0;
+ }
+
+ struct PgsqlRowDataEx
+ {
+ int32 deferred = 0;
+ }
+
+ union PgsqlFlushEx switch (uint8) extends core::stream::Extension
+ {
+ case 84: pgsql::stream::PgsqlTypeFlushEx type;
+ case 67: pgsql::stream::PgsqlCompletedFlushEx completion;
+ case 90: pgsql::stream::PgsqlReadyFlushEx ready;
+ }
+
+ enum PgsqlFormat (uint16)
+ {
+ TEXT(0),
+ BINARY(1)
+ }
+
+ struct PgsqlColumnInfo
+ {
+ string16 name;
+ int32 tableOid;
+ int16 index;
+ int32 typeOid;
+ int16 length;
+ int32 modifier;
+ PgsqlFormat format;
+ }
+
+ struct PgsqlTypeFlushEx
+ {
+ PgsqlColumnInfo[] columns;
+ }
+
+ struct PgsqlCompletedFlushEx
+ {
+ string16 tag;
+ }
+
+ enum PgsqlStatus (uint8)
+ {
+ IDLE(73),
+ TRANSACTION(84),
+ ERROR(69)
+ }
+
+ struct PgsqlReadyFlushEx
+ {
+ PgsqlStatus status;
+ }
+ }
+}
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/client.yaml b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/client.yaml
new file mode 100644
index 0000000000..971eccfc84
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/client.yaml
@@ -0,0 +1,22 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+---
+name: test
+bindings:
+ app0:
+ type: pgsql
+ kind: client
+ exit: net0
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/server.yaml b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/server.yaml
new file mode 100644
index 0000000000..e792a837e4
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/config/server.yaml
@@ -0,0 +1,22 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+---
+name: test
+bindings:
+ net0:
+ type: pgsql
+ kind: server
+ exit: app0
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/schema/pgsql.schema.patch.json b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/schema/pgsql.schema.patch.json
new file mode 100644
index 0000000000..de34cc7625
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/schema/pgsql.schema.patch.json
@@ -0,0 +1,50 @@
+[
+ {
+ "op": "add",
+ "path": "/$defs/binding/properties/type/enum/-",
+ "value": "pgsql"
+ },
+ {
+ "op": "add",
+ "path": "/$defs/binding/allOf/-",
+ "value":
+ {
+ "if":
+ {
+ "properties":
+ {
+ "type":
+ {
+ "const": "pgsql"
+ }
+ }
+ },
+ "then":
+ {
+ "properties":
+ {
+ "type":
+ {
+ "const": "pgsql"
+ },
+ "kind":
+ {
+ "enum": [ "client", "server" ]
+ },
+ "vault": false,
+ "options": false,
+ "routes": false
+ },
+ "anyOf":
+ [
+ {
+ "required":
+ [
+ "exit"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/client.rpt
new file mode 100644
index 0000000000..274501cfe1
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/client.rpt
@@ -0,0 +1,31 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read abort
+write aborted
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/server.rpt
new file mode 100644
index 0000000000..d750157216
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.read.abort/server.rpt
@@ -0,0 +1,35 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write aborted
+read abort
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/client.rpt
new file mode 100644
index 0000000000..edec5d4ef6
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/client.rpt
@@ -0,0 +1,31 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write abort
+read aborted
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/server.rpt
new file mode 100644
index 0000000000..3b4c24ea01
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/client.sent.write.abort/server.rpt
@@ -0,0 +1,35 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read aborted
+write abort
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/client.rpt
new file mode 100644
index 0000000000..3b4b648f7f
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/client.rpt
@@ -0,0 +1,58 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write option zilla:flags "init"
+write zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .deferred(41)
+ .build()
+ .build()}
+write "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, timestamp VARCHAR,"
+
+write option zilla:flags "fin"
+write " PRIMARY KEY (name));"
+ [0x00]
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("CREATE_TABLE")
+ .build()
+ .build()}
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/server.rpt
new file mode 100644
index 0000000000..575551430d
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.fragmented/server.rpt
@@ -0,0 +1,62 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read option zilla:flags "init"
+read zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .deferred(41)
+ .build()
+ .build()}
+read "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, timestamp VARCHAR,"
+
+read option zilla:flags "fin"
+read " PRIMARY KEY (name));"
+ [0x00]
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("CREATE_TABLE")
+ .build()
+ .build()}
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/client.rpt
new file mode 100644
index 0000000000..939265a72b
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/client.rpt
@@ -0,0 +1,55 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .build()
+ .build()}
+write "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, PRIMARY KEY (name));"
+ [0x00]
+
+write flush
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("CREATE_TABLE")
+ .build()
+ .build()}
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/server.rpt
new file mode 100644
index 0000000000..778b3043e7
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/create.table.with.primary.key/server.rpt
@@ -0,0 +1,57 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .build()
+ .build()}
+read "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, PRIMARY KEY (name));"
+ [0x00]
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("CREATE_TABLE")
+ .build()
+ .build()}
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/client.rpt
new file mode 100644
index 0000000000..abf8208ff1
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/client.rpt
@@ -0,0 +1,100 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .build()
+ .build()}
+write "SELECT * FROM balances;"
+ [0x00]
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .type()
+ .column()
+ .name("balance")
+ .tableOid(0)
+ .index(0)
+ .typeOid(701)
+ .length(8)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .column()
+ .name("timestamp")
+ .tableOid(0)
+ .index(0)
+ .typeOid(20)
+ .length(8)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .column()
+ .name("user_id")
+ .tableOid(0)
+ .index(0)
+ .typeOid(17)
+ .length(-1)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .build()
+ .build()}
+
+read zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .row()
+ .build()
+ .build()}
+read [0x00 0x03] # Field Count
+ [0x00 0x00 0x00 0x04] # Length
+ [0x31 0x39 0x36 0x34] # Data
+ [0x00 0x00 0x00 0x0a] # Length
+ [0x31 0x37 0x32 0x33 0x35 0x39 0x33 0x31 0x31 0x33] # Data
+ [0x00 0x00 0x00 0x04] # Length
+ [0x5c 0x78 0x33 0x31] # Data
+
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("SELECT 1")
+ .build()
+ .build()}
+
+read advised zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/server.rpt
new file mode 100644
index 0000000000..c0575c3658
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/select.table/server.rpt
@@ -0,0 +1,103 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .query()
+ .build()
+ .build()}
+read "SELECT * FROM balances;"
+read [0x00]
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .type()
+ .column()
+ .name("balance")
+ .tableOid(0)
+ .index(0)
+ .typeOid(701)
+ .length(8)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .column()
+ .name("timestamp")
+ .tableOid(0)
+ .index(0)
+ .typeOid(20)
+ .length(8)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .column()
+ .name("user_id")
+ .tableOid(0)
+ .index(0)
+ .typeOid(17)
+ .length(-1)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .build()
+ .build()}
+
+write zilla:data.ext ${pgsql:dataEx()
+ .typeId(zilla:id("pgsql"))
+ .row()
+ .build()
+ .build()}
+write [0x00 0x03] # Field Count
+ [0x00 0x00 0x00 0x04] # Length
+ [0x31 0x39 0x36 0x34] # Data
+ [0x00 0x00 0x00 0x0a] # Length
+ [0x31 0x37 0x32 0x33 0x35 0x39 0x33 0x31 0x31 0x33] # Data
+ [0x00 0x00 0x00 0x04] # Length
+ [0x5c 0x78 0x33 0x31] # Data
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .completion()
+ .tag("SELECT 1")
+ .build()
+ .build()}
+
+write advise zilla:flush ${pgsql:flushEx()
+ .typeId(zilla:id("pgsql"))
+ .ready()
+ .status("IDLE")
+ .build()
+ .build()}
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/client.rpt
new file mode 100644
index 0000000000..14d03b01ad
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/client.rpt
@@ -0,0 +1,31 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/server.rpt
new file mode 100644
index 0000000000..a614aa4e01
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/ssl.request/server.rpt
@@ -0,0 +1,35 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/client.rpt
new file mode 100644
index 0000000000..0e041a3c51
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/client.rpt
@@ -0,0 +1,31 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+write zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/server.rpt
new file mode 100644
index 0000000000..263b8175f5
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/application/termination.request/server.rpt
@@ -0,0 +1,35 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+
+accepted
+
+read zilla:begin.ext ${pgsql:beginEx()
+ .typeId(zilla:id("pgsql"))
+ .parameter("user", "root")
+ .parameter("database", "dev")
+ .parameter("application_name", "psql")
+ .parameter("client_encoding", "UTF8")
+ .build()}
+
+connected
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/client.rpt
new file mode 100644
index 0000000000..b7cf8dde76
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/client.rpt
@@ -0,0 +1,72 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read abort
+write aborted
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/server.rpt
new file mode 100644
index 0000000000..2fa4e897c4
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.read.abort/server.rpt
@@ -0,0 +1,74 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write aborted
+read abort
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/client.rpt
new file mode 100644
index 0000000000..21cc55c7bf
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/client.rpt
@@ -0,0 +1,72 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write abort
+read aborted
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/server.rpt
new file mode 100644
index 0000000000..0593b3e115
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/client.sent.write.abort/server.rpt
@@ -0,0 +1,74 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read aborted
+write abort
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/client.rpt
new file mode 100644
index 0000000000..2c0f9bca1d
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/client.rpt
@@ -0,0 +1,91 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x51] # type Q
+ 130 # length
+ "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR,"
+
+write " timestamp VARCHAR, PRIMARY KEY (name));"
+ [0x00] # End of string
+
+read [0x43] # type C
+ 17 # length
+ "CREATE_TABLE" # tag
+ [0x00] # End of string
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x58] # type X
+ 4 # length
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/server.rpt
new file mode 100644
index 0000000000..82f7d00e60
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.fragmented/server.rpt
@@ -0,0 +1,93 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x51] # type Q
+ 130 # length
+ "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR,"
+
+read " timestamp VARCHAR, PRIMARY KEY (name));"
+ [0x00] # End of string
+
+write [0x43] # type C
+ 17 # length
+ "CREATE_TABLE" # tag
+ [0x00] # End of string
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x58] # type X
+ 4 # length
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/client.rpt
new file mode 100644
index 0000000000..7a46c8a830
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/client.rpt
@@ -0,0 +1,89 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x51] # type Q
+ 111 # length
+ "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, PRIMARY KEY (name));"
+ [0x00] # End of string
+
+read [0x43] # type C
+ 17 # length
+ "CREATE_TABLE" # tag
+ [0x00] # End of string
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x58] # type X
+ 4 # length
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/server.rpt
new file mode 100644
index 0000000000..9d73f7dfdf
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/create.table.with.primary.key/server.rpt
@@ -0,0 +1,91 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x51] # type Q
+ 111 # length
+ "CREATE TABLE IF NOT EXISTS balances (name VARCHAR, type VARCHAR, description VARCHAR, PRIMARY KEY (name));"
+ [0x00] # End of string
+
+write [0x43] # type C
+ 17 # length
+ "CREATE_TABLE" # tag
+ [0x00] # End of string
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x58] # type X
+ 4 # length
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/client.rpt
new file mode 100644
index 0000000000..270d5f2556
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/client.rpt
@@ -0,0 +1,128 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x51] # type Q
+ 28 # length
+ "SELECT * FROM balances;"
+ [0x00]
+
+read [0x54] # type T
+ 86 # length
+ 3s # Field Count
+ "balance" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 701 # Type OID
+ 8s # Length
+ -1 # Modifier
+ 0s # Format
+ "timestamp" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 20 # Type OID
+ 8s # Length
+ -1 # Modifier
+ 0s # Format
+ "user_id" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 17 # Type OID
+ -1s # Length
+ -1 # Modifier
+ 0s # Format
+
+read [0x44] # type D
+ 36 # length
+ 3s # Field Count
+ 4 # Length
+ [0x31 0x39 0x36 0x34] # Data
+ 10 # Length
+ [0x31 0x37 0x32 0x33 0x35 0x39 0x33 0x31 0x31 0x33] # Data
+ 4 # Length
+ [0x5c 0x78 0x33 0x31] # Data
+
+
+read [0x43] # type C
+ 13 # length
+ "SELECT 1" # tag
+ [0x00] # End of string
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x58] # type X
+ 4 # length
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/server.rpt
new file mode 100644
index 0000000000..bdd4c46b05
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/select.table/server.rpt
@@ -0,0 +1,130 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x51] # type Q
+ 28 # length
+ "SELECT * FROM balances;"
+ [0x00]
+
+write [0x54] # type T
+ 86 # length
+ 3s # Field Count
+ "balance" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 701 # Type OID
+ 8s # Length
+ -1 # Modifier
+ 0s # Format
+ "timestamp" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 20 # Type OID
+ 8s # Length
+ -1 # Modifier
+ 0s # Format
+ "user_id" # Column name
+ [0x00] # End of string
+ 0 # Table OID
+ 0s # Index
+ 17 # Type OID
+ -1s # Length
+ -1 # Modifier
+ 0s # Format
+
+write [0x44] # type D
+ 36 # length
+ 3s # Field Count
+ 4 # Length
+ [0x31 0x39 0x36 0x34] # Data
+ 10 # Length
+ [0x31 0x37 0x32 0x33 0x35 0x39 0x33 0x31 0x31 0x33] # Data
+ 4 # Length
+ [0x5c 0x78 0x33 0x31] # Data
+
+
+write [0x43] # type C
+ 13 # length
+ "SELECT 1" # tag
+ [0x00] # End of string
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x58] # type X
+ 4 # length
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/client.rpt
new file mode 100644
index 0000000000..d52779fc06
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/client.rpt
@@ -0,0 +1,80 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 8 # length
+ [0x04 0xd2 0x16 0x2f] # request code
+
+read [0x4e]
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x58] # type X
+ 4 # length
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/server.rpt
new file mode 100644
index 0000000000..e0b126ef8e
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/ssl.request/server.rpt
@@ -0,0 +1,82 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 8 # length
+ [0x04 0xd2 0x16 0x2f] # request code
+
+write [0x4e]
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x58] # type X
+ 4 # length
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/client.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/client.rpt
new file mode 100644
index 0000000000..ba792966fe
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/client.rpt
@@ -0,0 +1,75 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkConnectWindow 8192
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+read [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+read [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+read [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+read [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+read [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+read [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+read [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+write [0x58] # type X
+ 4 # length
+
+write close
+read closed
diff --git a/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/server.rpt b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/server.rpt
new file mode 100644
index 0000000000..68391ef8ea
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/main/scripts/io/aklivity/zilla/specs/binding/pgsql/streams/network/termination.request/server.rpt
@@ -0,0 +1,77 @@
+#
+# Copyright 2021-2023 Aklivity Inc
+#
+# Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 75 # length
+ 3s # major version
+ 0s # minor version
+ "user" [0x00] # name
+ "root" [0x00] # value
+ "database" [0x00] # name
+ "dev" [0x00] # value
+ "application_name" [0x00] # name
+ "psql" [0x00] # value
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+ [0x00] # end of parameters
+
+write [0x52] # type R
+ 8 # length
+ 0 # authentication type
+
+write [0x4b] # type K
+ 12 # length
+ 0 # pid
+ 0 # key
+
+write [0x53] # type S
+ 25 # length
+ "client_encoding" [0x00] # name
+ "UTF8" [0x00] # value
+
+write [0x53] # type S
+ 35 # length
+ "standard_conforming_strings" [0x00] # name
+ [0x6f 0x6e 0x00] # value
+
+write [0x53] # type S
+ 25 # length
+ "server_version" [0x00] # name
+ "1.0.0" [0x00] # value
+
+write [0x53] # type S
+ 27 # length
+ "application_name" [0x00] # name
+ "zilla" [0x00] # value
+
+write [0x5a] # type Z
+ 5 # length
+ [0x49] # status
+
+read [0x58] # type X
+ 4 # length
+
+read closed
+write close
diff --git a/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/ApplicationIT.java b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/ApplicationIT.java
new file mode 100644
index 0000000000..7f88a05062
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/ApplicationIT.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.specs.binding.pgsql.streams;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.rules.RuleChain.outerRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+
+import io.aklivity.k3po.runtime.junit.annotation.Specification;
+import io.aklivity.k3po.runtime.junit.rules.K3poRule;
+
+public class ApplicationIT
+{
+ private final K3poRule k3po = new K3poRule()
+ .addScriptRoot("app", "io/aklivity/zilla/specs/binding/pgsql/streams/application");
+
+ private final TestRule timeout = new DisableOnDebug(new Timeout(10, SECONDS));
+
+ @Rule
+ public final TestRule chain = outerRule(k3po).around(timeout);
+
+ @Test
+ @Specification({
+ "${app}/create.table.with.primary.key/client",
+ "${app}/create.table.with.primary.key/server"
+ })
+ public void shouldCreateTableWithPrimaryKey() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/select.table/client",
+ "${app}/select.table/server" })
+ public void shouldSelectTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/client.sent.write.abort/client",
+ "${app}/client.sent.write.abort/server" })
+ public void shouldHandleClientSentWriteAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/client.sent.read.abort/client",
+ "${app}/client.sent.read.abort/server" })
+ public void shouldHandleClientSentReadAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/create.table.fragmented/client",
+ "${app}/create.table.fragmented/server" })
+ public void shouldHandleFragmentedCreateTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/ssl.request/client",
+ "${app}/ssl.request/server" })
+ public void shouldHandleSslRequest() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/termination.request/client",
+ "${app}/termination.request/server" })
+ public void shouldHandleTerminationRequest() throws Exception
+ {
+ k3po.finish();
+ }
+}
diff --git a/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/NetworkIT.java b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/NetworkIT.java
new file mode 100644
index 0000000000..89eed98a61
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/NetworkIT.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.specs.binding.pgsql.streams;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.rules.RuleChain.outerRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+
+import io.aklivity.k3po.runtime.junit.annotation.Specification;
+import io.aklivity.k3po.runtime.junit.rules.K3poRule;
+
+public class NetworkIT
+{
+ private final K3poRule k3po = new K3poRule()
+ .addScriptRoot("net", "io/aklivity/zilla/specs/binding/pgsql/streams/network");
+
+ private final TestRule timeout = new DisableOnDebug(new Timeout(10, SECONDS));
+
+ @Rule
+ public final TestRule chain = outerRule(k3po).around(timeout);
+
+ @Test
+ @Specification({
+ "${net}/create.table.with.primary.key/client",
+ "${net}/create.table.with.primary.key/server"
+ })
+ public void shouldCreateTableWithPrimaryKey() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/select.table/client",
+ "${net}/select.table/server" })
+ public void shouldSelectTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/ssl.request/client",
+ "${net}/ssl.request/server" })
+ public void shouldHandleSslRequest() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/client.sent.write.abort/client",
+ "${net}/client.sent.write.abort/server" })
+ public void shouldHandleClientSentWriteAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/client.sent.read.abort/client",
+ "${net}/client.sent.read.abort/server" })
+ public void shouldHandleClientSentReadAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/create.table.fragmented/client",
+ "${net}/create.table.fragmented/server" })
+ public void shouldHandleFragmentedCreateTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/termination.request/client",
+ "${net}/termination.request/server" })
+ public void shouldHandleTerminationRequest() throws Exception
+ {
+ k3po.finish();
+ }
+}
diff --git a/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/internal/PgsqlFunctionsTest.java b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/internal/PgsqlFunctionsTest.java
new file mode 100644
index 0000000000..0367fe9dbc
--- /dev/null
+++ b/incubator/binding-pgsql.spec/src/test/java/io/aklivity/zilla/specs/binding/pgsql/streams/internal/PgsqlFunctionsTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.specs.binding.pgsql.streams.internal;
+
+import static io.aklivity.zilla.specs.binding.pgsql.PgsqlFunctions.beginEx;
+import static io.aklivity.zilla.specs.binding.pgsql.PgsqlFunctions.dataEx;
+import static io.aklivity.zilla.specs.binding.pgsql.PgsqlFunctions.flushEx;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.lang.reflect.Method;
+
+import javax.el.ELContext;
+import javax.el.FunctionMapper;
+
+import org.agrona.DirectBuffer;
+import org.agrona.concurrent.UnsafeBuffer;
+import org.junit.Test;
+
+import io.aklivity.k3po.runtime.lang.internal.el.ExpressionContext;
+import io.aklivity.zilla.specs.binding.pgsql.PgsqlFunctions;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlBeginExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlDataExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlFlushExFW;
+import io.aklivity.zilla.specs.binding.pgsql.internal.types.stream.PgsqlStatus;
+
+
+public class PgsqlFunctionsTest
+{
+ @Test
+ public void shouldResolveFunction() throws Exception
+ {
+ final ELContext ctx = new ExpressionContext();
+ final FunctionMapper mapper = ctx.getFunctionMapper();
+ final Method function = mapper.resolveFunction("pgsql", "beginEx");
+
+ assertNotNull(function);
+ assertSame(PgsqlFunctions.class, function.getDeclaringClass());
+ }
+
+ @Test
+ public void shouldGenerateBeginExtension()
+ {
+ byte[] build = beginEx()
+ .typeId(0x01)
+ .parameter("name", "pgsql")
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlBeginExFW beginEx = new PgsqlBeginExFW().wrap(buffer, 0, buffer.capacity());
+
+ assertEquals(1, beginEx.parameters().fieldCount());
+ beginEx.parameters().forEach(h ->
+ {
+ assertEquals("name\u0000", h.name().asString());
+ assertEquals("pgsql\u0000", h.value().asString());
+ });
+ }
+
+ @Test
+ public void shouldEncodePgsqlDataQueryExtension()
+ {
+ final byte[] build = dataEx()
+ .typeId(0x01)
+ .query()
+ .deferred(1)
+ .build()
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlDataExFW dataEx = new PgsqlDataExFW().wrap(buffer, 0, buffer.capacity());
+ assertEquals(0x01, dataEx.typeId());
+
+ assertEquals(1, dataEx.query().deferred());
+ }
+
+ @Test
+ public void shouldEncodePgsqlDataRowExtension()
+ {
+ final byte[] build = dataEx()
+ .typeId(0x01)
+ .row()
+ .deferred(1)
+ .build()
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlDataExFW dataEx = new PgsqlDataExFW().wrap(buffer, 0, buffer.capacity());
+ assertEquals(0x01, dataEx.typeId());
+
+ assertEquals(1, dataEx.row().deferred());
+ }
+
+ @Test
+ public void shouldEncodePgsqlFlushTypeExtension()
+ {
+ final byte[] build = flushEx()
+ .typeId(0x01)
+ .type()
+ .column()
+ .name("balance")
+ .tableOid(0)
+ .index((short) 0)
+ .typeOid(701)
+ .length((short)8)
+ .modifier(-1)
+ .format("TEXT")
+ .build()
+ .build()
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlFlushExFW flushEx = new PgsqlFlushExFW().wrap(buffer, 0, buffer.capacity());
+ assertEquals(0x01, flushEx.typeId());
+
+ assertEquals(1, flushEx.type().columns().fieldCount());
+ flushEx.type().columns().forEach(c ->
+ {
+ assertEquals("balance\u0000", c.name().asString());
+ assertEquals(0, c.tableOid());
+ assertEquals(0, c.index());
+ assertEquals(701, c.typeOid());
+ assertEquals(-1, c.modifier());
+ });
+ }
+
+ @Test
+ public void shouldEncodePgsqlFlushCompletionExtension()
+ {
+ final byte[] build = flushEx()
+ .typeId(0x01)
+ .completion()
+ .tag("CREATE_TABLE")
+ .build()
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlFlushExFW flushEx = new PgsqlFlushExFW().wrap(buffer, 0, buffer.capacity());
+
+ assertEquals(0x01, flushEx.typeId());
+ assertEquals("CREATE_TABLE\u0000", flushEx.completion().tag().asString());
+ }
+
+ @Test
+ public void shouldEncodePgsqlFlushReadyExtension()
+ {
+ final byte[] build = flushEx()
+ .typeId(0x01)
+ .ready()
+ .status("IDLE")
+ .build()
+ .build();
+
+ DirectBuffer buffer = new UnsafeBuffer(build);
+ PgsqlFlushExFW flushEx = new PgsqlFlushExFW().wrap(buffer, 0, buffer.capacity());
+
+ assertEquals(0x01, flushEx.typeId());
+ assertEquals(PgsqlStatus.IDLE, flushEx.ready().status().get());
+ }
+
+}
diff --git a/incubator/binding-pgsql/COPYRIGHT b/incubator/binding-pgsql/COPYRIGHT
new file mode 100644
index 0000000000..0cb10b6f62
--- /dev/null
+++ b/incubator/binding-pgsql/COPYRIGHT
@@ -0,0 +1,12 @@
+Copyright ${copyrightYears} Aklivity Inc
+
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
diff --git a/incubator/binding-pgsql/LICENSE b/incubator/binding-pgsql/LICENSE
new file mode 100644
index 0000000000..f6abb6327b
--- /dev/null
+++ b/incubator/binding-pgsql/LICENSE
@@ -0,0 +1,114 @@
+ Aklivity Community License Agreement
+ Version 1.0
+
+This Aklivity Community License Agreement Version 1.0 (the “Agreement”) sets
+forth the terms on which Aklivity, Inc. (“Aklivity”) makes available certain
+software made available by Aklivity under this Agreement (the “Software”). BY
+INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF THE SOFTWARE,
+YOU AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO
+SUCH TERMS AND CONDITIONS, YOU MUST NOT USE THE SOFTWARE. IF YOU ARE RECEIVING
+THE SOFTWARE ON BEHALF OF A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU
+HAVE THE ACTUAL AUTHORITY TO AGREE TO THE TERMS AND CONDITIONS OF THIS
+AGREEMENT ON BEHALF OF SUCH ENTITY. “Licensee” means you, an individual, or
+the entity on whose behalf you are receiving the Software.
+
+ 1. LICENSE GRANT AND CONDITIONS.
+
+ 1.1 License. Subject to the terms and conditions of this Agreement,
+ Aklivity hereby grants to Licensee a non-exclusive, royalty-free,
+ worldwide, non-transferable, non-sublicenseable license during the term
+ of this Agreement to: (a) use the Software; (b) prepare modifications and
+ derivative works of the Software; (c) distribute the Software (including
+ without limitation in source code or object code form); and (d) reproduce
+ copies of the Software (the “License”). Licensee is not granted the
+ right to, and Licensee shall not, exercise the License for an Excluded
+ Purpose. For purposes of this Agreement, “Excluded Purpose” means making
+ available any software-as-a-service, platform-as-a-service,
+ infrastructure-as-a-service or other similar online service that competes
+ with Aklivity products or services that provide the Software.
+
+ 1.2 Conditions. In consideration of the License, Licensee’s distribution
+ of the Software is subject to the following conditions:
+
+ (a) Licensee must cause any Software modified by Licensee to carry
+ prominent notices stating that Licensee modified the Software.
+
+ (b) On each Software copy, Licensee shall reproduce and not remove or
+ alter all Aklivity or third party copyright or other proprietary
+ notices contained in the Software, and Licensee must provide the
+ notice below with each copy.
+
+ “This software is made available by Aklivity, Inc., under the
+ terms of the Aklivity Community License Agreement, Version 1.0
+ located at http://www.Aklivity.io/Aklivity-community-license. BY
+ INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF
+ THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT.”
+
+ 1.3 Licensee Modifications. Licensee may add its own copyright notices
+ to modifications made by Licensee and may provide additional or different
+ license terms and conditions for use, reproduction, or distribution of
+ Licensee’s modifications. While redistributing the Software or
+ modifications thereof, Licensee may choose to offer, for a fee or free of
+ charge, support, warranty, indemnity, or other obligations. Licensee, and
+ not Aklivity, will be responsible for any such obligations.
+
+ 1.4 No Sublicensing. The License does not include the right to
+ sublicense the Software, however, each recipient to which Licensee
+ provides the Software may exercise the Licenses so long as such recipient
+ agrees to the terms and conditions of this Agreement.
+
+ 2. TERM AND TERMINATION. This Agreement will continue unless and until
+ earlier terminated as set forth herein. If Licensee breaches any of its
+ conditions or obligations under this Agreement, this Agreement will
+ terminate automatically and the License will terminate automatically and
+ permanently.
+
+ 3. INTELLECTUAL PROPERTY. As between the parties, Aklivity will retain all
+ right, title, and interest in the Software, and all intellectual property
+ rights therein. Aklivity hereby reserves all rights not expressly granted
+ to Licensee in this Agreement. Aklivity hereby reserves all rights in its
+ trademarks and service marks, and no licenses therein are granted in this
+ Agreement.
+
+ 4. DISCLAIMER. Aklivity HEREBY DISCLAIMS ANY AND ALL WARRANTIES AND
+ CONDITIONS, EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, AND SPECIFICALLY
+ DISCLAIMS ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE, WITH RESPECT TO THE SOFTWARE.
+
+ 5. LIMITATION OF LIABILITY. Aklivity WILL NOT BE LIABLE FOR ANY DAMAGES OF
+ ANY KIND, INCLUDING BUT NOT LIMITED TO, LOST PROFITS OR ANY CONSEQUENTIAL,
+ SPECIAL, INCIDENTAL, INDIRECT, OR DIRECT DAMAGES, HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, ARISING OUT OF THIS AGREEMENT. THE FOREGOING SHALL
+ APPLY TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+
+ 6.GENERAL.
+
+ 6.1 Governing Law. This Agreement will be governed by and interpreted in
+ accordance with the laws of the state of California, without reference to
+ its conflict of laws principles. If Licensee is located within the
+ United States, all disputes arising out of this Agreement are subject to
+ the exclusive jurisdiction of courts located in Santa Clara County,
+ California. USA. If Licensee is located outside of the United States,
+ any dispute, controversy or claim arising out of or relating to this
+ Agreement will be referred to and finally determined by arbitration in
+ accordance with the JAMS International Arbitration Rules. The tribunal
+ will consist of one arbitrator. The place of arbitration will be Palo
+ Alto, California. The language to be used in the arbitral proceedings
+ will be English. Judgment upon the award rendered by the arbitrator may
+ be entered in any court having jurisdiction thereof.
+
+ 6.2 Assignment. Licensee is not authorized to assign its rights under
+ this Agreement to any third party. Aklivity may freely assign its rights
+ under this Agreement to any third party.
+
+ 6.3 Other. This Agreement is the entire agreement between the parties
+ regarding the subject matter hereof. No amendment or modification of
+ this Agreement will be valid or binding upon the parties unless made in
+ writing and signed by the duly authorized representatives of both
+ parties. In the event that any provision, including without limitation
+ any condition, of this Agreement is held to be unenforceable, this
+ Agreement and all licenses and rights granted hereunder will immediately
+ terminate. Waiver by Aklivity of a breach of any provision of this
+ Agreement or the failure by Aklivity to exercise any right hereunder
+ will not be construed as a waiver of any subsequent breach of that right
+ or as a waiver of any other right.
\ No newline at end of file
diff --git a/incubator/binding-pgsql/NOTICE b/incubator/binding-pgsql/NOTICE
new file mode 100644
index 0000000000..9024d8926d
--- /dev/null
+++ b/incubator/binding-pgsql/NOTICE
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+
diff --git a/incubator/binding-pgsql/NOTICE.template b/incubator/binding-pgsql/NOTICE.template
new file mode 100644
index 0000000000..209ca12f74
--- /dev/null
+++ b/incubator/binding-pgsql/NOTICE.template
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+#GENERATED_NOTICES#
diff --git a/incubator/binding-pgsql/mvnw b/incubator/binding-pgsql/mvnw
new file mode 100755
index 0000000000..d2f0ea3808
--- /dev/null
+++ b/incubator/binding-pgsql/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/incubator/binding-pgsql/mvnw.cmd b/incubator/binding-pgsql/mvnw.cmd
new file mode 100644
index 0000000000..b26ab24f03
--- /dev/null
+++ b/incubator/binding-pgsql/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/incubator/binding-pgsql/pom.xml b/incubator/binding-pgsql/pom.xml
new file mode 100644
index 0000000000..ab39dd7b2f
--- /dev/null
+++ b/incubator/binding-pgsql/pom.xml
@@ -0,0 +1,233 @@
+
+
+
+ 4.0.0
+
+ io.aklivity.zilla
+ incubator
+ 0.9.93
+ ../pom.xml
+
+
+ binding-pgsql
+ zilla::incubator::binding-pgsql
+
+
+
+ Aklivity Community License Agreement
+ https://www.aklivity.io/aklivity-community-license/
+ repo
+
+
+
+
+ 0.89
+ 0
+
+
+
+
+ ${project.groupId}
+ binding-pgsql.spec
+ ${project.version}
+ provided
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ provided
+
+
+ ${project.groupId}
+ engine
+ test-jar
+ ${project.version}
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ io.aklivity.k3po
+ control-junit
+ test
+
+
+ io.aklivity.k3po
+ lang
+ test
+
+
+ org.openjdk.jmh
+ jmh-core
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ test
+
+
+
+
+
+
+ org.jasig.maven
+ maven-notice-plugin
+
+
+ ${project.groupId}
+ flyweight-maven-plugin
+ ${project.version}
+
+ core pgsql protocol
+ io.aklivity.zilla.runtime.binding.pgsql.internal.types
+
+
+
+
+ generate
+
+
+
+
+
+ com.mycila
+ license-maven-plugin
+
+
+ maven-checkstyle-plugin
+
+
+ maven-dependency-plugin
+
+
+ process-resources
+
+ unpack
+
+
+
+
+ ${project.groupId}
+ binding-pgsql.spec
+
+
+ ^\Qio/aklivity/zilla/specs/binding/pgsql/\E
+ io/aklivity/zilla/runtime/binding/pgsql/internal/
+
+
+
+
+ io/aklivity/zilla/specs/binding/pgsql/schema/pgsql.schema.patch.json
+ ${project.build.directory}/classes
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+ io.aklivity.k3po
+ k3po-maven-plugin
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ test-jar
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ io/aklivity/zilla/runtime/binding/pgsql/internal/types/**/*.class
+
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ ${jacoco.coverage.ratio}
+
+
+ CLASS
+ MISSEDCOUNT
+ ${jacoco.missed.count}
+
+
+
+
+
+
+
+ io.gatling
+ maven-shade-plugin
+
+
+
+ org.agrona:agrona
+ io.aklivity.zilla:engine
+ org.openjdk.jmh:jmh-core
+ net.sf.jopt-simple:jopt-simple
+ org.apache.commons:commons-math3
+ commons-cli:commons-cli
+ com.github.biboudis:jmh-profilers
+
+
+
+
+
+
+
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBinding.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBinding.java
new file mode 100644
index 0000000000..7dcc630942
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBinding.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal;
+
+import java.net.URL;
+
+import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.binding.Binding;
+
+public final class PgsqlBinding implements Binding
+{
+ public static final String NAME = "pgsql";
+
+ private final PgsqlConfiguration config;
+
+ PgsqlBinding(
+ PgsqlConfiguration config)
+ {
+ this.config = config;
+ }
+
+ @Override
+ public String name()
+ {
+ return PgsqlBinding.NAME;
+ }
+
+ @Override
+ public URL type()
+ {
+ return getClass().getResource("schema/pgsql.schema.patch.json");
+ }
+
+ @Override
+ public PgsqlBindingContext supply(
+ EngineContext context)
+ {
+ return new PgsqlBindingContext(config, context);
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingContext.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingContext.java
new file mode 100644
index 0000000000..6d05623bdb
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingContext.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal;
+
+import static io.aklivity.zilla.runtime.engine.config.KindConfig.CLIENT;
+import static io.aklivity.zilla.runtime.engine.config.KindConfig.SERVER;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import io.aklivity.zilla.runtime.binding.pgsql.internal.stream.PgsqlClientFactory;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.stream.PgsqlServerFactory;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.stream.PgsqlStreamFactory;
+import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.binding.BindingContext;
+import io.aklivity.zilla.runtime.engine.binding.BindingHandler;
+import io.aklivity.zilla.runtime.engine.config.BindingConfig;
+import io.aklivity.zilla.runtime.engine.config.KindConfig;
+
+final class PgsqlBindingContext implements BindingContext
+{
+ private final Map factories;
+
+ PgsqlBindingContext(
+ PgsqlConfiguration config,
+ EngineContext context)
+ {
+ final EnumMap factories = new EnumMap<>(KindConfig.class);
+ factories.put(SERVER, new PgsqlServerFactory(config, context));
+ factories.put(CLIENT, new PgsqlClientFactory(config, context));
+ this.factories = factories;
+ }
+
+ @Override
+ public BindingHandler attach(
+ BindingConfig binding)
+ {
+ PgsqlStreamFactory factory = factories.get(binding.kind);
+
+ if (factory != null)
+ {
+ factory.attach(binding);
+ }
+
+ return factory;
+ }
+
+ @Override
+ public void detach(
+ BindingConfig binding)
+ {
+ PgsqlStreamFactory factory = factories.get(binding.kind);
+
+ if (factory != null)
+ {
+ factory.detach(binding.id);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s %s", getClass().getSimpleName(), factories);
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingFactorySpi.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingFactorySpi.java
new file mode 100644
index 0000000000..6429dacdf8
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlBindingFactorySpi.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal;
+
+import io.aklivity.zilla.runtime.common.feature.Incubating;
+import io.aklivity.zilla.runtime.engine.Configuration;
+import io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi;
+
+@Incubating
+public final class PgsqlBindingFactorySpi implements BindingFactorySpi
+{
+ @Override
+ public String type()
+ {
+ return PgsqlBinding.NAME;
+ }
+
+ @Override
+ public PgsqlBinding create(
+ Configuration config)
+ {
+ return new PgsqlBinding(new PgsqlConfiguration(config));
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlConfiguration.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlConfiguration.java
new file mode 100644
index 0000000000..0d73bddf30
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/PgsqlConfiguration.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal;
+
+import io.aklivity.zilla.runtime.engine.Configuration;
+
+public class PgsqlConfiguration extends Configuration
+{
+ private static final ConfigurationDef PGSQL_CONFIG;
+
+ static
+ {
+ final ConfigurationDef config = new ConfigurationDef(String.format("zilla.binding.%s", PgsqlBinding.NAME));
+ PGSQL_CONFIG = config;
+ }
+
+ public PgsqlConfiguration(
+ Configuration config)
+ {
+ super(PGSQL_CONFIG, config);
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlBindingConfig.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlBindingConfig.java
new file mode 100644
index 0000000000..20afd0fa42
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlBindingConfig.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.config;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.List;
+
+import io.aklivity.zilla.runtime.engine.config.BindingConfig;
+import io.aklivity.zilla.runtime.engine.config.KindConfig;
+
+public final class PgsqlBindingConfig
+{
+ public final long id;
+ public final String name;
+ public final KindConfig kind;
+ public final List routes;
+
+ public PgsqlBindingConfig(
+ BindingConfig binding)
+ {
+ this.id = binding.id;
+ this.name = binding.name;
+ this.kind = binding.kind;
+ this.routes = binding.routes.stream().map(PgsqlRouteConfig::new).collect(toList());
+ }
+
+ public PgsqlRouteConfig resolve(
+ long authorization)
+ {
+ return routes.stream()
+ .filter(r -> r.authorized(authorization))
+ .findFirst()
+ .orElse(null);
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlRouteConfig.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlRouteConfig.java
new file mode 100644
index 0000000000..1be96cb669
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/config/PgsqlRouteConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.config;
+
+import java.util.function.LongPredicate;
+
+import io.aklivity.zilla.runtime.engine.config.RouteConfig;
+
+public final class PgsqlRouteConfig
+{
+ public final long id;
+ public final int order;
+
+ private final LongPredicate authorized;
+
+ public PgsqlRouteConfig(
+ RouteConfig route)
+ {
+ this.id = route.id;
+ this.order = route.order;
+ this.authorized = route.authorized;
+ }
+
+ boolean authorized(
+ long authorization)
+ {
+ return authorized.test(authorization);
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlClientFactory.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlClientFactory.java
new file mode 100644
index 0000000000..b3626ef285
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlClientFactory.java
@@ -0,0 +1,1681 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+import static io.aklivity.zilla.runtime.engine.buffer.BufferPool.NO_SLOT;
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static java.util.Objects.requireNonNull;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.LongUnaryOperator;
+
+import org.agrona.DirectBuffer;
+import org.agrona.MutableDirectBuffer;
+import org.agrona.collections.Int2ObjectHashMap;
+import org.agrona.collections.Long2ObjectHashMap;
+import org.agrona.collections.LongLongConsumer;
+import org.agrona.collections.MutableInteger;
+import org.agrona.concurrent.UnsafeBuffer;
+
+import io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlBinding;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlConfiguration;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.config.PgsqlBindingConfig;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.config.PgsqlRouteConfig;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.Array32FW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.OctetsFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlAuthenticationMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlBackendKeyMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlRowDescriptionFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlStartupMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.AbortFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.BeginFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.DataFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.EndFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.ExtensionFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.FlushFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlBeginExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlColumnInfoFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlDataExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlFlushExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlFormat;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlQueryDataExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlStatus;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.ResetFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.WindowFW;
+import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.binding.BindingHandler;
+import io.aklivity.zilla.runtime.engine.binding.function.MessageConsumer;
+import io.aklivity.zilla.runtime.engine.buffer.BufferPool;
+import io.aklivity.zilla.runtime.engine.config.BindingConfig;
+
+
+public final class PgsqlClientFactory implements PgsqlStreamFactory
+{
+ private static final Byte MESSAGE_TYPE_TYPE = 'T';
+ private static final Byte MESSAGE_TYPE_AUTH = 'R';
+ private static final Byte MESSAGE_TYPE_BACKEND_KEY = 'K';
+ private static final Byte MESSAGE_TYPE_PARAMETER_STATUS = 'S';
+ private static final Byte MESSAGE_TYPE_QUERY = 'Q';
+ private static final Byte MESSAGE_TYPE_DATA_ROW = 'D';
+ private static final Byte MESSAGE_TYPE_COMPLETION = 'C';
+ private static final Byte MESSAGE_TYPE_READY = 'Z';
+ private static final Byte MESSAGE_TYPE_TERMINATION = 'X';
+
+ private static final int AUTHENTICATION_SUCCESS_CODE = 0;
+ private static final int END_OF_FIELD = 0x00;
+
+ private static final int FLAGS_INIT = 0x02;
+ private static final int FLAGS_CONT = 0x00;
+ private static final int FLAGS_FIN = 0x01;
+ private static final int FLAGS_COMP = 0x03;
+
+ private static final DirectBuffer EMPTY_BUFFER = new UnsafeBuffer(new byte[0]);
+ private static final OctetsFW EMPTY_OCTETS = new OctetsFW().wrap(EMPTY_BUFFER, 0, 0);
+ private static final Consumer EMPTY_EXTENSION = ex -> {};
+
+ private final MutableInteger payloadRemaining = new MutableInteger(0);
+
+ private final BeginFW beginRO = new BeginFW();
+ private final DataFW dataRO = new DataFW();
+ private final EndFW endRO = new EndFW();
+ private final AbortFW abortRO = new AbortFW();
+
+ private final BeginFW.Builder beginRW = new BeginFW.Builder();
+ private final DataFW.Builder dataRW = new DataFW.Builder();
+ private final EndFW.Builder endRW = new EndFW.Builder();
+ private final AbortFW.Builder abortRW = new AbortFW.Builder();
+ private final FlushFW.Builder flushRW = new FlushFW.Builder();
+
+ private final ResetFW resetRO = new ResetFW();
+ private final WindowFW windowRO = new WindowFW();
+
+ private final ResetFW.Builder resetRW = new ResetFW.Builder();
+ private final WindowFW.Builder windowRW = new WindowFW.Builder();
+
+ private final ExtensionFW extensionRO = new ExtensionFW();
+ private final PgsqlBeginExFW pgsqlBeginExRO = new PgsqlBeginExFW();
+ private final PgsqlDataExFW pgsqlDataExRO = new PgsqlDataExFW();
+
+ private final PgsqlDataExFW.Builder dataExRW = new PgsqlDataExFW.Builder();
+ private final PgsqlFlushExFW.Builder flushExRW = new PgsqlFlushExFW.Builder();
+ private final Array32FW.Builder columnsRW =
+ new Array32FW.Builder<>(new PgsqlColumnInfoFW.Builder(), new PgsqlColumnInfoFW());
+
+ private final PgsqlMessageFW messageRO = new PgsqlMessageFW();
+ private final PgsqlAuthenticationMessageFW authMessageRO = new PgsqlAuthenticationMessageFW();
+ private final PgsqlBackendKeyMessageFW backendKeyMessageRO = new PgsqlBackendKeyMessageFW();
+ private final PgsqlRowDescriptionFW rowDescriptionRO = new PgsqlRowDescriptionFW();
+
+ private final PgsqlStartupMessageFW.Builder startupMessageRW = new PgsqlStartupMessageFW.Builder();
+ private final PgsqlMessageFW.Builder messageRW = new PgsqlMessageFW.Builder();
+
+ private final BufferPool bufferPool;
+ private final MutableDirectBuffer writeBuffer;
+ private final MutableDirectBuffer messageBuffer;
+ private final LongUnaryOperator supplyInitialId;
+ private final LongUnaryOperator supplyReplyId;
+ private final BindingHandler streamFactory;
+
+ private final int decodeMax;
+
+ private final Long2ObjectHashMap bindings;
+ private final int pgsqlTypeId;
+
+ private final PgsqlClientDecoder decodePgsqlMessage = this::decodePgsqlMessage;
+ private final PgsqlClientDecoder decodePgsqlAuth = this::decodePgsqlMessageAuth;
+ private final PgsqlClientDecoder decodePgsqlBackendKey = this::decodePgsqlMessageBackendKey;
+ private final PgsqlClientDecoder decodePgsqlParameterStatus = this::decodePgsqlMessageParameterStatus;
+ private final PgsqlClientDecoder decodePgsqlType = this::decodePgsqlMessageType;
+ private final PgsqlClientDecoder decodePgsqlRow = this::decodePgsqlMessageRow;
+ private final PgsqlClientDecoder decodePgsqlCompletion = this::decodePgsqlMessageCompletion;
+ private final PgsqlClientDecoder decodePgsqlReady = this::decodePgsqlMessageReady;
+ private final PgsqlClientDecoder decodePgsqlTermination = this::decodePgsqlMessageTerminator;
+ private final PgsqlClientDecoder decodePgsqlPayload = this::decodePgsqlMessagePayload;
+ private final PgsqlClientDecoder decodePgsqlIgnoreOne = this::decodePgsqlIgnoreOne;
+ private final PgsqlClientDecoder decodePgsqlIgnoreAll = this::decodePgsqlIgnoreAll;
+
+ private final Int2ObjectHashMap decodersByType;
+
+ {
+ Int2ObjectHashMap decodersByType = new Int2ObjectHashMap<>();
+ decodersByType.put(MESSAGE_TYPE_AUTH, decodePgsqlAuth);
+ decodersByType.put(MESSAGE_TYPE_BACKEND_KEY, decodePgsqlBackendKey);
+ decodersByType.put(MESSAGE_TYPE_PARAMETER_STATUS, decodePgsqlParameterStatus);
+ decodersByType.put(MESSAGE_TYPE_TYPE, decodePgsqlType);
+ decodersByType.put(MESSAGE_TYPE_DATA_ROW, decodePgsqlRow);
+ decodersByType.put(MESSAGE_TYPE_COMPLETION, decodePgsqlCompletion);
+ decodersByType.put(MESSAGE_TYPE_READY, decodePgsqlReady);
+ decodersByType.put(MESSAGE_TYPE_TERMINATION, decodePgsqlTermination);
+ this.decodersByType = decodersByType;
+ }
+
+ public PgsqlClientFactory(
+ PgsqlConfiguration config,
+ EngineContext context)
+ {
+ this.writeBuffer = requireNonNull(context.writeBuffer());
+ this.messageBuffer = new UnsafeBuffer(new byte[writeBuffer.capacity()]);
+ this.supplyInitialId = context::supplyInitialId;
+ this.supplyReplyId = context::supplyReplyId;
+ this.streamFactory = context.streamFactory();
+ this.bufferPool = context.bufferPool();
+ this.decodeMax = bufferPool.slotCapacity();
+
+ this.bindings = new Long2ObjectHashMap<>();
+
+ this.pgsqlTypeId = context.supplyTypeId(PgsqlBinding.NAME);
+ }
+
+ @Override
+ public void attach(
+ BindingConfig binding)
+ {
+ PgsqlBindingConfig pgsqlBinding = new PgsqlBindingConfig(binding);
+ bindings.put(binding.id, pgsqlBinding);
+ }
+
+ @Override
+ public void detach(
+ long bindingId)
+ {
+ bindings.remove(bindingId);
+ }
+
+ @Override
+ public MessageConsumer newStream(
+ int msgTypeId,
+ DirectBuffer buffer,
+ int index,
+ int length,
+ MessageConsumer network)
+ {
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ final long originId = begin.originId();
+ final long routedId = begin.routedId();
+ final long initialId = begin.streamId();
+ final long authorization = begin.authorization();
+ final OctetsFW extension = begin.extension();
+ final PgsqlBeginExFW pgsqlBeginEx = extension.get(pgsqlBeginExRO::tryWrap);
+
+ final Map parameters = new LinkedHashMap<>();
+ pgsqlBeginEx.parameters().forEach(p -> parameters.put(p.name().asString(), p.value().asString()));
+
+ PgsqlBindingConfig binding = bindings.get(routedId);
+
+ MessageConsumer newStream = null;
+
+ if (binding != null)
+ {
+ PgsqlRouteConfig route = binding.resolve(authorization);
+
+ if (route != null)
+ {
+ newStream = new PgsqlStream(
+ network,
+ originId,
+ routedId,
+ initialId,
+ route.id,
+ parameters)::onApplicationMessage;
+ }
+ }
+
+ return newStream;
+ }
+
+ private final class PgsqlClient
+ {
+ private final Map parameters;
+ private final PgsqlStream stream;
+ private LongLongConsumer encoder;
+ private PgsqlClientDecoder decoder;
+
+ private MessageConsumer network;
+ private final long originId;
+ private final long routedId;
+ private long authorization;
+
+ private final long initialId;
+ private final long replyId;
+
+ private long initialSeq;
+ private long initialAck;
+ private int initialMax;
+ public long initialBudgetId;
+ private int initialPadding;
+
+ private long replySeq;
+ private long replyAck;
+ private int replyMax;
+ private int replyPad;
+ private long replyBudgetId;
+
+ private int decodeSlot = NO_SLOT;
+ private int decodeSlotOffset;
+ private int decodeSlotReserved;
+
+ private int state;
+
+ private PgsqlClient(
+ PgsqlStream stream,
+ long originId,
+ long routedId,
+ Map parameters)
+ {
+ this.originId = originId;
+ this.routedId = routedId;
+ this.initialId = supplyInitialId.applyAsLong(routedId);
+ this.replyId = supplyReplyId.applyAsLong(initialId);
+ this.replyMax = decodeMax;
+ this.parameters = parameters;
+
+ this.stream = stream;
+ this.encoder = this::doEncodeNetworkStartupMessage;
+ this.decoder = decodePgsqlMessage;
+ }
+
+ private void onNetworkMessage(
+ final int msgTypeId,
+ final DirectBuffer buffer,
+ final int index,
+ final int length)
+ {
+ switch (msgTypeId)
+ {
+ case BeginFW.TYPE_ID:
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ onNetworkBegin(begin);
+ break;
+ case DataFW.TYPE_ID:
+ final DataFW data = dataRO.wrap(buffer, index, index + length);
+ onNetworkData(data);
+ break;
+ case EndFW.TYPE_ID:
+ final EndFW end = endRO.wrap(buffer, index, index + length);
+ onNetworkEnd(end);
+ break;
+ case AbortFW.TYPE_ID:
+ final AbortFW abort = abortRO.wrap(buffer, index, index + length);
+ onNetworkAbort(abort);
+ break;
+ case ResetFW.TYPE_ID:
+ final ResetFW reset = resetRO.wrap(buffer, index, index + length);
+ onNetworkReset(reset);
+ break;
+ case WindowFW.TYPE_ID:
+ final WindowFW window = windowRO.wrap(buffer, index, index + length);
+ onNetworkWindow(window);
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ private void onNetworkBegin(
+ final BeginFW begin)
+ {
+ final long sequence = begin.sequence();
+ final long acknowledge = begin.acknowledge();
+ final int maximum = begin.maximum();
+ final long traceId = begin.traceId();
+ final long authorization = begin.authorization();
+
+ assert acknowledge == sequence;
+ assert sequence >= replySeq;
+ assert maximum == 0;
+
+ replySeq = sequence;
+ replyAck = acknowledge;
+
+ state = PgsqlState.openingReply(state);
+
+ doNetworkWindow(traceId, authorization, replyBudgetId, decodeSlotReserved, replyPad);
+
+ encoder.accept(traceId, authorization);
+ }
+
+ private void onNetworkData(
+ final DataFW data)
+ {
+ final long sequence = data.sequence();
+ final long acknowledge = data.acknowledge();
+ final long traceId = data.traceId();
+ final long budgetId = data.budgetId();
+ int reserved = data.reserved();
+ authorization = data.authorization();
+
+ assert acknowledge <= sequence;
+ assert sequence >= replySeq;
+ assert acknowledge <= replyAck;
+
+ replySeq = sequence + reserved;
+
+ assert replyAck <= replySeq;
+
+ if (replySeq > replyAck + decodeMax)
+ {
+ cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final OctetsFW payload = data.payload();
+ DirectBuffer buffer = payload.buffer();
+ int offset = payload.offset();
+ int limit = payload.limit();
+
+ if (decodeSlot != NO_SLOT)
+ {
+ final MutableDirectBuffer slotBuffer = bufferPool.buffer(decodeSlot);
+ slotBuffer.putBytes(decodeSlotOffset, buffer, offset, limit - offset);
+ decodeSlotOffset += limit - offset;
+ decodeSlotReserved += reserved;
+
+ buffer = slotBuffer;
+ offset = 0;
+ limit = decodeSlotOffset;
+ reserved = decodeSlotReserved;
+ }
+
+ decodeNetwork(traceId, authorization, budgetId, reserved, buffer, offset, limit);
+ }
+ }
+
+ private void onNetworkEnd(
+ final EndFW end)
+ {
+ final long traceId = end.traceId();
+ final long authorization = end.authorization();
+
+ state = PgsqlState.closeReply(state);
+
+ cleanupDecodeSlotIfNecessary();
+
+ stream.doApplicationEnd(traceId, authorization);
+ }
+
+ private void onNetworkAbort(
+ final AbortFW abort)
+ {
+ final long traceId = abort.traceId();
+ final long authorization = abort.authorization();
+
+ state = PgsqlState.closeReply(state);
+
+ stream.doApplicationAbort(traceId, authorization);
+ }
+
+ private void onNetworkReset(
+ final ResetFW reset)
+ {
+ final long traceId = reset.traceId();
+ final long authorization = reset.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ stream.doApplicationReset(traceId, authorization);
+ }
+
+ private void onNetworkWindow(
+ final WindowFW window)
+ {
+ final long sequence = window.sequence();
+ final long acknowledge = window.acknowledge();
+ final long traceId = window.traceId();
+ final long authorization = window.authorization();
+ final long budgetId = window.budgetId();
+ final int maximum = window.maximum();
+ final int padding = window.padding();
+
+ assert acknowledge <= sequence;
+ assert sequence <= initialSeq;
+ assert acknowledge >= initialAck;
+ assert maximum >= initialMax;
+
+ initialAck = acknowledge;
+ initialMax = maximum;
+ initialBudgetId = budgetId;
+ initialPadding = padding;
+
+ assert initialAck <= initialSeq;
+
+ state = PgsqlState.openInitial(state);
+
+ encoder.accept(traceId, authorization);
+ }
+
+ private void doNetworkBegin(
+ long traceId,
+ long authorization,
+ long affinity)
+ {
+ state = PgsqlState.openingInitial(state);
+
+ final BeginFW begin = beginRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(initialId)
+ .sequence(initialSeq)
+ .acknowledge(initialAck)
+ .maximum(initialMax)
+ .traceId(traceId)
+ .authorization(authorization)
+ .affinity(affinity)
+ .extension(EMPTY_OCTETS)
+ .build();
+
+ network = streamFactory.newStream(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof(),
+ this::onNetworkMessage);
+
+ network.accept(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof());
+ }
+
+ private void doNetworkWindow(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int pendingAck,
+ int paddingMin)
+ {
+ long replyAckMax = Math.max(replySeq - pendingAck, replyAck);
+ if (!PgsqlState.replyOpening(stream.state) ||
+ replyAckMax > replyAck || stream.replyMax > replyMax)
+ {
+ replyAck = replyAckMax;
+ replyMax = Math.max(stream.replyMax, replyMax);
+ replyPad = paddingMin;
+ assert replyAck <= replySeq;
+
+ state = PgsqlState.openReply(state);
+
+ doWindow(network, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, budgetId, replyPad);
+ }
+ }
+
+ private void doNetworkData(
+ long traceId,
+ long authorization,
+ int flags,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int length)
+ {
+ final int reserved = length + initialPadding;
+
+ doData(network, originId, routedId, initialId, initialSeq, initialAck, initialMax, traceId, authorization,
+ flags, budgetId, reserved, buffer, offset, length, EMPTY_EXTENSION);
+
+ initialSeq += reserved;
+ }
+
+ private void doNetworkResetAndAbort(
+ long traceId,
+ long authorization)
+ {
+ doNetworkReset(traceId, authorization);
+ doNetworkAbort(traceId, authorization);
+ }
+
+ private void doNetworkEnd(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeInitial(state);
+
+ doEnd(network, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doNetworkAbort(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeReply(state);
+
+ doAbort(network, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doNetworkReset(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closingInitial(state);
+
+ doReset(network, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+
+ cleanupDecodeSlotIfNecessary();
+ }
+
+ private int decodeNetwork(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int reserved,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ PgsqlClientDecoder previous = null;
+ int progress = offset;
+ while (progress <= limit && previous != decoder)
+ {
+ previous = decoder;
+ progress = decoder.decode(this, traceId, authorization, budgetId, buffer, progress, limit);
+ }
+
+ if (progress < limit)
+ {
+ if (decodeSlot == NO_SLOT)
+ {
+ decodeSlot = bufferPool.acquire(replyId);
+ }
+
+ if (decodeSlot == NO_SLOT)
+ {
+ cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final MutableDirectBuffer decodeBuffer = bufferPool.buffer(decodeSlot);
+ decodeBuffer.putBytes(0, buffer, progress, limit - progress);
+ decodeSlotOffset = limit - progress;
+ decodeSlotReserved = (int)((long) reserved * (limit - progress) / (limit - offset));
+ }
+ }
+ else
+ {
+ cleanupDecodeSlotIfNecessary();
+ }
+
+ if (!PgsqlState.replyClosed(state))
+ {
+ doNetworkWindow(traceId, authorization, budgetId, decodeSlotReserved, replyPad);
+ }
+
+ return progress;
+ }
+
+ private void onDecodeMessageType(
+ long traceId,
+ long authorization,
+ Array32FW columns)
+ {
+ Consumer typeEx = e -> e.set((b, o, l) -> flushExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .type(t -> t.columns(columns))
+ .build().sizeof());
+
+ stream.doApplicationFlush(traceId, authorization, decodeSlotReserved, typeEx);
+ }
+
+ private void doEncodeNetworkWindow(
+ long traceId,
+ long authorization)
+ {
+ stream.doApplicationWindow(traceId, authorization, initialBudgetId,
+ (int)(initialSeq - initialAck), initialPadding);
+ }
+
+ private void doEncodeNetworkStartupMessage(
+ long traceId,
+ long authorization)
+ {
+ if (PgsqlState.replyOpening(state))
+ {
+ int startupOffset = 0;
+
+ PgsqlStartupMessageFW startupMessage =
+ startupMessageRW.wrap(messageBuffer, startupOffset, messageBuffer.capacity())
+ .length(0)
+ .majorVersion(3)
+ .minorVersion(0)
+ .build();
+ startupOffset = startupMessage.limit();
+
+ for (Map.Entry parameter : parameters.entrySet())
+ {
+ messageBuffer.putBytes(startupOffset, parameter.getKey().getBytes());
+ startupOffset += parameter.getKey().length();
+ messageBuffer.putBytes(startupOffset, parameter.getValue().getBytes());
+ startupOffset += parameter.getValue().length();
+ }
+
+ messageBuffer.putByte(startupOffset, (byte) END_OF_FIELD);
+ startupOffset += Byte.BYTES;
+
+ messageBuffer.putInt(0, startupOffset, BIG_ENDIAN);
+
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, startupOffset);
+
+ encoder = this::doEncodeNetworkWindow;
+ }
+ }
+
+ private void onDecodeMessageRow(
+ long traceId,
+ long authorization,
+ int flags,
+ int deferred,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ Consumer rowEx = e -> e.set((b, o, l) -> dataExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .row(q -> q.deferred(deferred))
+ .build().sizeof());
+
+ stream.doApplicationData(traceId, authorization, flags, buffer, offset, limit, rowEx);
+ }
+
+ private void onDecodeMessageCompletion(
+ long traceId,
+ long authorization,
+ DirectBuffer buffer,
+ int offset,
+ int length)
+ {
+ Consumer completionEx = e -> e.set((b, o, l) -> flushExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .completion(c -> c.tag(buffer, offset, length))
+ .build().sizeof());
+
+ stream.doApplicationFlush(traceId, authorization, decodeSlotReserved, completionEx);
+ }
+
+ private void onDecodeMessageReady(
+ long traceId,
+ long authorization,
+ PgsqlStatus status)
+ {
+ encoder = this::doEncodeNetworkWindow;
+ encoder.accept(traceId, authorization);
+
+ if (PgsqlState.replyOpening(stream.state))
+ {
+ Consumer readyEx = e -> e.set((b, o, l) -> flushExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .ready(r -> r.status(s -> s.set(status)))
+ .build().sizeof());
+
+ stream.doApplicationFlush(traceId, authorization, decodeSlotReserved, readyEx);
+ }
+ else
+ {
+ stream.doApplicationBegin(traceId, authorization);
+ }
+
+ }
+
+ private void onDecodeMessageTermination(
+ long traceId,
+ long authorization)
+ {
+ doNetworkEnd(traceId, authorization);
+ stream.doApplicationEnd(traceId, authorization);
+ }
+
+ private void cleanupNetwork(
+ long traceId,
+ long authorization)
+ {
+ cleanup(traceId, authorization, this::doNetworkResetAndAbort);
+ }
+
+ private void cleanup(
+ long traceId,
+ long authorization,
+ LongLongConsumer cleanupHandler)
+ {
+ cleanupHandler.accept(traceId, authorization);
+ stream.doApplicationAbortAndReset(traceId, authorization);
+ }
+
+ private void cleanupDecodeSlotIfNecessary()
+ {
+ if (decodeSlot != NO_SLOT)
+ {
+ bufferPool.release(decodeSlot);
+ decodeSlot = NO_SLOT;
+ decodeSlotOffset = 0;
+ decodeSlotReserved = 0;
+ }
+ }
+ }
+
+ private final class PgsqlStream
+ {
+ private final PgsqlClient client;
+
+ private MessageConsumer application;
+
+ private final long initialId;
+ private final long replyId;
+ private final long originId;
+ private final long routedId;
+
+ private long initialSeq;
+ private long initialAck;
+ private int initialMax;
+ private int initialPad;
+ private long initialBudgetId;
+
+ private long replySeq;
+ private long replyAck;
+ private int replyMax;
+ private int replyPadding;
+ private long replyBudgetId;
+
+ private int state;
+
+ private PgsqlStream(
+ MessageConsumer application,
+ long originId,
+ long routedId,
+ long initialId,
+ long resolvedId,
+ Map parameters)
+ {
+ this.application = application;
+ this.originId = originId;
+ this.routedId = routedId;
+ this.initialId = initialId;
+ this.replyId = supplyReplyId.applyAsLong(initialId);
+
+ this.client = new PgsqlClient(this, originId, resolvedId, parameters);
+ }
+
+ private void onApplicationMessage(
+ final int msgTypeId,
+ final DirectBuffer buffer,
+ final int index,
+ final int length)
+ {
+ switch (msgTypeId)
+ {
+ case BeginFW.TYPE_ID:
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ onApplicationBegin(begin);
+ break;
+ case DataFW.TYPE_ID:
+ final DataFW data = dataRO.wrap(buffer, index, index + length);
+ onApplicationData(data);
+ break;
+ case EndFW.TYPE_ID:
+ final EndFW end = endRO.wrap(buffer, index, index + length);
+ onApplicationEnd(end);
+ break;
+ case AbortFW.TYPE_ID:
+ final AbortFW abort = abortRO.wrap(buffer, index, index + length);
+ onApplicationAbort(abort);
+ break;
+ case ResetFW.TYPE_ID:
+ final ResetFW reset = resetRO.wrap(buffer, index, index + length);
+ onApplicationReset(reset);
+ break;
+ case WindowFW.TYPE_ID:
+ final WindowFW window = windowRO.wrap(buffer, index, index + length);
+ onApplicationWindow(window);
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ private void onApplicationBegin(
+ final BeginFW begin)
+ {
+ final long traceId = begin.traceId();
+ final long authorization = begin.authorization();
+ final long affinity = begin.affinity();
+
+ state = PgsqlState.openingInitial(state);
+
+ client.doNetworkBegin(traceId, authorization, affinity);
+ }
+
+ private void onApplicationData(
+ final DataFW data)
+ {
+ final long sequence = data.sequence();
+ final long acknowledge = data.acknowledge();
+ final long traceId = data.traceId();
+ final long authorization = data.authorization();
+ final long budgetId = data.budgetId();
+ final int reserved = data.reserved();
+
+ assert acknowledge <= sequence;
+ assert sequence >= initialSeq;
+ assert acknowledge <= initialAck;
+ assert budgetId == client.initialBudgetId;
+
+ initialSeq = sequence + reserved;
+
+ assert initialAck <= initialSeq;
+
+ if (initialSeq > initialAck + initialMax)
+ {
+ client.cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final OctetsFW extension = data.extension();
+ final OctetsFW payload = data.payload();
+ final ExtensionFW dataEx = extension.get(extensionRO::tryWrap);
+
+ final PgsqlDataExFW pgsqlDataEx = dataEx != null && dataEx.typeId() == pgsqlTypeId ?
+ extension.get(pgsqlDataExRO::tryWrap) : null;
+
+ if (pgsqlDataEx != null &&
+ pgsqlDataEx.kind() == PgsqlDataExFW.KIND_QUERY)
+ {
+ PgsqlQueryDataExFW query = pgsqlDataEx.query();
+ doEncodeQuery(traceId, authorization, query.deferred(), payload);
+ }
+ else
+ {
+ client.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, payload.value(), 0, payload.sizeof());
+ }
+ }
+ }
+
+ private void onApplicationEnd(
+ final EndFW end)
+ {
+ final long traceId = end.traceId();
+ final long authorization = end.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ if (!PgsqlState.closed(client.state))
+ {
+ doEncodeTermination(traceId, authorization);
+ client.doNetworkEnd(traceId, authorization);
+ }
+ }
+
+ private void onApplicationAbort(
+ final AbortFW abort)
+ {
+ final long traceId = abort.traceId();
+ final long authorization = abort.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ client.doNetworkAbort(traceId, authorization);
+ }
+
+ private void onApplicationReset(
+ final ResetFW reset)
+ {
+ final long traceId = reset.traceId();
+ final long authorization = reset.authorization();
+
+ state = PgsqlState.closeReply(state);
+
+ client.doNetworkReset(traceId, authorization);
+ }
+
+ private void onApplicationWindow(
+ final WindowFW window)
+ {
+ final long sequence = window.sequence();
+ final long acknowledge = window.acknowledge();
+ final int maximum = window.maximum();
+ final long traceId = window.traceId();
+ final long authorization = window.authorization();
+ final long budgetId = window.budgetId();
+ final int padding = window.padding();
+
+ assert acknowledge <= sequence;
+ assert acknowledge >= replyAck;
+ assert maximum + acknowledge >= replyMax + replyAck;
+
+ replyBudgetId = budgetId;
+ replyAck = acknowledge;
+ replyMax = maximum;
+ replyPadding = padding;
+
+ assert replyAck <= replySeq;
+
+ client.doNetworkWindow(traceId, authorization, budgetId, (int)(replySeq - replyAck), replyPadding);
+ }
+
+ private void doApplicationBegin(
+ long traceId,
+ long authorization)
+ {
+ doBegin(application, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, 0L, EMPTY_OCTETS);
+
+ state = PgsqlState.openingReply(state);
+ }
+
+ private void doApplicationData(
+ long traceId,
+ long authorization,
+ int flags,
+ DirectBuffer buffer,
+ int offset,
+ int limit,
+ Consumer extension)
+ {
+ final int length = limit - offset;
+ final int reserved = length + initialPad;
+
+ doData(application, originId, routedId, replyId, replySeq, replyAck, replyMax, traceId, authorization,
+ flags, replyBudgetId, reserved, buffer, offset, length, extension);
+
+ replySeq += reserved;
+ assert replySeq <= replyAck + replyMax;
+ }
+
+ private void doApplicationEnd(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeInitial(state);
+
+ doEnd(application, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doApplicationAbort(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeReply(state);
+
+ doAbort(application, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doApplicationReset(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeInitial(state);
+
+ doReset(application, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doApplicationAbortAndReset(
+ long traceId,
+ long authorization)
+ {
+ doApplicationAbort(traceId, authorization);
+ doApplicationReset(traceId, authorization);
+ }
+
+ private void doApplicationWindow(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int pendingAck,
+ int paddingMin)
+ {
+ long initialAckMax = Math.max(initialSeq - pendingAck, initialAck);
+ if (initialAckMax > initialAck || client.initialMax > initialMax)
+ {
+ initialAck = initialAckMax;
+ initialMax = client.initialMax;
+ assert initialAck <= initialSeq;
+
+ int initialPad = paddingMin;
+
+ state = PgsqlState.openInitial(state);
+
+ doWindow(application, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, budgetId, initialPad);
+ }
+ }
+
+ public void doApplicationFlush(
+ long traceId,
+ long authorization,
+ int reserved,
+ Consumer extension)
+ {
+ replySeq = client.replySeq;
+
+ doFlush(application, originId, routedId, replyId, replySeq, replyAck, replyMax, traceId,
+ authorization, replyBudgetId, reserved, extension);
+ }
+
+ private void doEncodeQuery(
+ long traceId,
+ long authorization,
+ int deferred,
+ OctetsFW query)
+ {
+ final DirectBuffer queryBuffer = query.value();
+ final int rowSize = queryBuffer.capacity();
+
+ int queryOffset = 0;
+
+ PgsqlMessageFW messageQuery = messageRW.wrap(messageBuffer, queryOffset, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_QUERY)
+ .length(rowSize + Integer.BYTES + deferred)
+ .build();
+ queryOffset = messageQuery.limit();
+
+ messageBuffer.putBytes(queryOffset, queryBuffer, 0, rowSize);
+ queryOffset += rowSize;
+
+ client.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, queryOffset);
+ }
+
+ private void doEncodeTermination(
+ long traceId,
+ long authorization)
+ {
+ PgsqlMessageFW messageTermination = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_TERMINATION)
+ .length(Integer.BYTES)
+ .build();
+
+ client.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, messageTermination.limit());
+ }
+ }
+
+ private void doBegin(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final long affinity,
+ final OctetsFW extension)
+ {
+ final BeginFW begin = beginRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .affinity(affinity)
+ .extension(extension)
+ .build();
+
+ receiver.accept(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof());
+ }
+
+ private void doData(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final int flags,
+ final long budgetId,
+ final int reserved,
+ DirectBuffer buffer,
+ int offset,
+ int length,
+ Consumer extension)
+ {
+ final DataFW data = dataRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .flags(flags)
+ .budgetId(budgetId)
+ .reserved(reserved)
+ .payload(buffer, offset, length)
+ .extension(extension)
+ .build();
+
+ receiver.accept(data.typeId(), data.buffer(), data.offset(), data.sizeof());
+ }
+
+ private void doFlush(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final long budgetId,
+ final int reserved,
+ Consumer extension)
+ {
+ final FlushFW flush = flushRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .budgetId(budgetId)
+ .reserved(reserved)
+ .extension(extension)
+ .build();
+
+ receiver.accept(flush.typeId(), flush.buffer(), flush.offset(), flush.sizeof());
+ }
+
+ private void doAbort(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final AbortFW abort = abortRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ receiver.accept(abort.typeId(), abort.buffer(), abort.offset(), abort.sizeof());
+ }
+
+ private void doEnd(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final EndFW end = endRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ receiver.accept(end.typeId(), end.buffer(), end.offset(), end.sizeof());
+ }
+
+ private void doReset(
+ final MessageConsumer sender,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final ResetFW reset = resetRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ sender.accept(reset.typeId(), reset.buffer(), reset.offset(), reset.sizeof());
+ }
+
+ private void doWindow(
+ final MessageConsumer sender,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ long authorization,
+ final long budgetId,
+ final int padding)
+ {
+ final WindowFW window = windowRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .budgetId(budgetId)
+ .padding(padding)
+ .build();
+
+ sender.accept(window.typeId(), window.buffer(), window.offset(), window.sizeof());
+ }
+
+ private int decodePgsqlMessage(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progress = offset;
+
+ final PgsqlMessageFW pgsqlMessage = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlMessage != null)
+ {
+ final int type = pgsqlMessage.type();
+
+ final PgsqlClientDecoder decoder = decodersByType.getOrDefault(type, decodePgsqlIgnoreOne);
+ client.decoder = decoder;
+ }
+
+ return progress;
+ }
+
+ private int decodePgsqlMessageAuth(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progress = offset;
+
+ final PgsqlAuthenticationMessageFW pgsqlAuth = authMessageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlAuth != null)
+ {
+ client.decoder = pgsqlAuth.authenticationType() == AUTHENTICATION_SUCCESS_CODE
+ ? decodePgsqlBackendKey
+ : decodePgsqlIgnoreAll;
+ progress = pgsqlAuth.limit();
+ }
+
+ return progress;
+ }
+
+ private int decodePgsqlMessageBackendKey(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progress = offset;
+
+ final PgsqlBackendKeyMessageFW pgsqlBackendKey = backendKeyMessageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlBackendKey != null)
+ {
+ client.decoder = decodePgsqlParameterStatus;
+ progress = pgsqlBackendKey.limit();
+ }
+
+ return progress;
+ }
+
+ private int decodePgsqlMessageParameterStatus(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progress = offset;
+
+ final PgsqlMessageFW pgsqlParameter = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlParameter != null)
+ {
+ client.decoder = decodePgsqlMessage;
+ progress += pgsqlParameter.length() + Byte.BYTES;
+ }
+
+ return progress;
+ }
+
+
+ private int decodePgsqlMessageType(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlType = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlType != null)
+ {
+ progressOffset = pgsqlType.limit();
+
+ short fieldCount = buffer.getShort(progressOffset, BIG_ENDIAN);
+ progressOffset += Short.BYTES;
+
+ columnsRW.wrap(messageBuffer, 0, messageBuffer.capacity());
+
+ columns:
+ for (int i = 0; i < fieldCount; i++)
+ {
+ final int nameOffset = progressOffset;
+ final int nameLength = getLengthOfString(buffer, progressOffset);
+
+ if (nameLength == -1)
+ {
+ break columns;
+ }
+
+ progressOffset += nameLength;
+
+ PgsqlRowDescriptionFW description = rowDescriptionRO.tryWrap(buffer, progressOffset, limit);
+
+ if (description == null)
+ {
+ break columns;
+ }
+
+ columnsRW.item(c -> c
+ .name(buffer, nameOffset, nameLength)
+ .tableOid(description.tableOid())
+ .index(description.index())
+ .typeOid(description.typeOid())
+ .length(description.length())
+ .modifier(description.modifier())
+ .format(f -> f.set(PgsqlFormat.valueOf(description.format()))));
+
+ progressOffset = description.limit();
+ }
+
+ Array32FW columns = columnsRW.build();
+
+ if (columns.fieldCount() == fieldCount)
+ {
+ client.onDecodeMessageType(traceId, authorization, columns);
+
+ client.decoder = decodePgsqlMessage;
+ }
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessageRow(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlRow = messageRO.tryWrap(buffer, progressOffset, limit);
+ if (pgsqlRow != null)
+ {
+ progressOffset = pgsqlRow.limit();
+
+ final int rowSize = pgsqlRow.length() - Integer.BYTES;
+ payloadRemaining.set(rowSize);
+
+ final int length = Math.min(payloadRemaining.value, limit - progressOffset);
+
+ if (length > 0)
+ {
+ final int flags = rowSize == length ? FLAGS_COMP : FLAGS_INIT;
+ final int deferred = rowSize - length;
+
+ client.onDecodeMessageRow(traceId, authorization, flags, deferred,
+ buffer, progressOffset, progressOffset + length);
+ progressOffset += length;
+ payloadRemaining.set(rowSize - length);
+
+ assert payloadRemaining.get() >= 0;
+
+ client.decoder = rowSize == length
+ ? decodePgsqlMessage
+ : decodePgsqlPayload;
+ }
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessageCompletion(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlCompletion = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlCompletion != null)
+ {
+ progressOffset = pgsqlCompletion.limit();
+
+ final int completionSize = pgsqlCompletion.length() - Integer.BYTES;
+ payloadRemaining.set(completionSize);
+ final int length = Math.min(payloadRemaining.value, limit - progressOffset);
+
+ client.onDecodeMessageCompletion(traceId, authorization, buffer, progressOffset, length);
+ progressOffset += length;
+ payloadRemaining.set(completionSize - length);
+
+ client.decoder = completionSize == length
+ ? decodePgsqlMessage
+ : decodePgsqlPayload;
+ }
+
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessageReady(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlReady = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlReady != null &&
+ limit - pgsqlReady.limit() >= Byte.BYTES)
+ {
+ progressOffset = pgsqlReady.limit();
+
+ client.onDecodeMessageReady(traceId, authorization, PgsqlStatus.valueOf(buffer.getByte(progressOffset)));
+ progressOffset += Byte.BYTES;
+
+ client.decoder = decodePgsqlMessage;
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessageTerminator(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlTerminate = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlTerminate != null)
+ {
+ client.onDecodeMessageTermination(traceId, authorization);
+
+ client.decoder = decodePgsqlIgnoreAll;
+ progressOffset = limit;
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessagePayload(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ final int payloadSize = payloadRemaining.get();
+ final int length = Math.min(payloadSize, limit - offset);
+
+ final int flags = payloadSize == length ? FLAGS_FIN : FLAGS_CONT;
+
+ client.stream.doApplicationData(traceId, authorization, flags, buffer, offset, offset + limit,
+ EMPTY_EXTENSION);
+ payloadRemaining.set(payloadSize - length);
+
+ assert payloadRemaining.get() >= 0;
+
+ if (payloadSize == length)
+ {
+ client.decoder = decodePgsqlMessage;
+ }
+
+ return length;
+ }
+
+ private int decodePgsqlIgnoreOne(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ final PgsqlMessageFW messageType = messageRO.wrap(buffer, offset, limit);
+ final int progress = messageType.limit() + messageType.length();
+
+ client.decoder = decodePgsqlMessage;
+ return progress;
+ }
+
+ private int decodePgsqlIgnoreAll(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ return limit;
+ }
+
+ @FunctionalInterface
+ private interface PgsqlClientDecoder
+ {
+ int decode(
+ PgsqlClient client,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit);
+ }
+
+ private int getLengthOfString(
+ DirectBuffer buffer,
+ int offset)
+ {
+ int length = -1;
+ loop:
+ for (int progress = offset; progress < buffer.capacity(); progress++)
+ {
+ if (buffer.getByte(progress) == END_OF_FIELD)
+ {
+ length = progress - offset + 1;
+ break loop;
+ }
+ }
+ return length;
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlServerFactory.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlServerFactory.java
new file mode 100644
index 0000000000..9c843b17ea
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlServerFactory.java
@@ -0,0 +1,1650 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+import static io.aklivity.zilla.runtime.engine.buffer.BufferPool.NO_SLOT;
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Consumer;
+import java.util.function.LongUnaryOperator;
+
+import org.agrona.DirectBuffer;
+import org.agrona.MutableDirectBuffer;
+import org.agrona.collections.Int2ObjectHashMap;
+import org.agrona.collections.Long2ObjectHashMap;
+import org.agrona.collections.LongLongConsumer;
+import org.agrona.collections.MutableInteger;
+import org.agrona.concurrent.UnsafeBuffer;
+
+import io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlBinding;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlConfiguration;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.config.PgsqlBindingConfig;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.config.PgsqlRouteConfig;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.Array32FW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.OctetsFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlAuthenticationMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlBackendKeyMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlCancelRequestMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlSslRequestFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlSslResponseFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.codec.PgsqlStartupMessageFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.AbortFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.BeginFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.DataFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.EndFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.ExtensionFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.FlushFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlBeginExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlCompletedFlushExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlDataExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlFlushExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlParameterFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlReadyFlushExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlStatus;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.PgsqlTypeFlushExFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.ResetFW;
+import io.aklivity.zilla.runtime.binding.pgsql.internal.types.stream.WindowFW;
+import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.binding.BindingHandler;
+import io.aklivity.zilla.runtime.engine.binding.function.MessageConsumer;
+import io.aklivity.zilla.runtime.engine.buffer.BufferPool;
+import io.aklivity.zilla.runtime.engine.config.BindingConfig;
+
+public final class PgsqlServerFactory implements PgsqlStreamFactory
+{
+ private static final Byte MESSAGE_TYPE_TYPE = 'T';
+ private static final Byte MESSAGE_TYPE_QUERY = 'Q';
+ private static final Byte MESSAGE_TYPE_DATA_ROW = 'D';
+ private static final Byte MESSAGE_TYPE_COMPLETION = 'C';
+ private static final Byte MESSAGE_TYPE_READY = 'Z';
+ private static final Byte MESSAGE_TYPE_TERMINATE = 'X';
+ private static final Byte MESSAGE_TYPE_PARAMETER_STATUS = 'S';
+
+ private static final int SSL_REQUEST_CODE = 80877103;
+ private static final int CANCEL_REQUEST_CODE = 80877102;
+ private static final int END_OF_FIELD = 0x00;
+
+ private static final int FLAGS_INIT = 0x02;
+ private static final int FLAGS_CONT = 0x00;
+ private static final int FLAGS_FIN = 0x01;
+ private static final int FLAGS_COMP = 0x03;
+
+ private static final DirectBuffer EMPTY_BUFFER = new UnsafeBuffer(new byte[0]);
+ private static final OctetsFW EMPTY_OCTETS = new OctetsFW().wrap(EMPTY_BUFFER, 0, 0);
+ private static final Consumer EMPTY_EXTENSION = ex -> {};
+
+ private final MutableInteger payloadRemaining = new MutableInteger(0);
+ private final MutableInteger progress = new MutableInteger(0);
+
+ private final BeginFW beginRO = new BeginFW();
+ private final DataFW dataRO = new DataFW();
+ private final EndFW endRO = new EndFW();
+ private final AbortFW abortRO = new AbortFW();
+ private final FlushFW flushRO = new FlushFW();
+
+ private final BeginFW.Builder beginRW = new BeginFW.Builder();
+ private final DataFW.Builder dataRW = new DataFW.Builder();
+ private final EndFW.Builder endRW = new EndFW.Builder();
+ private final AbortFW.Builder abortRW = new AbortFW.Builder();
+
+ private final ResetFW resetRO = new ResetFW();
+ private final WindowFW windowRO = new WindowFW();
+
+ private final ResetFW.Builder resetRW = new ResetFW.Builder();
+ private final WindowFW.Builder windowRW = new WindowFW.Builder();
+
+ private final ExtensionFW extensionRO = new ExtensionFW();
+ private final PgsqlDataExFW pgsqlDataExRO = new PgsqlDataExFW();
+ private final PgsqlFlushExFW pgsqlFlushExRO = new PgsqlFlushExFW();
+
+ private final PgsqlBeginExFW.Builder beginExRW = new PgsqlBeginExFW.Builder();
+ private final PgsqlDataExFW.Builder dataExRW = new PgsqlDataExFW.Builder();
+
+ private final PgsqlMessageFW messageRO = new PgsqlMessageFW();
+ private final PgsqlSslRequestFW sslRequestRO = new PgsqlSslRequestFW();
+ private final PgsqlStartupMessageFW startupMessageRO = new PgsqlStartupMessageFW();
+ private final PgsqlCancelRequestMessageFW cancelReqMessageRO = new PgsqlCancelRequestMessageFW();
+
+ private final PgsqlMessageFW.Builder messageRW = new PgsqlMessageFW.Builder();
+ private final PgsqlSslResponseFW.Builder sslResponseRW = new PgsqlSslResponseFW.Builder();
+ private final PgsqlAuthenticationMessageFW.Builder authMessageRW = new PgsqlAuthenticationMessageFW.Builder();
+ private final PgsqlBackendKeyMessageFW.Builder backendKeyMessageRW = new PgsqlBackendKeyMessageFW.Builder();
+
+ private final Array32FW.Builder parametersRW =
+ new Array32FW.Builder<>(new PgsqlParameterFW.Builder(), new PgsqlParameterFW());
+
+ private final BufferPool bufferPool;
+ private final MutableDirectBuffer writeBuffer;
+ private final MutableDirectBuffer messageBuffer;
+ private final LongUnaryOperator supplyInitialId;
+ private final LongUnaryOperator supplyReplyId;
+ private final BindingHandler streamFactory;
+
+ private final int decodeMax;
+
+ private final Long2ObjectHashMap bindings;
+ private final int pgsqlTypeId;
+
+ private final PgsqlServerDecoder decodePgsqlInitial = this::decodePgsqlInitial;
+ private final PgsqlServerDecoder decodePgsqlSslRequest = this::decodePgsqlSslRequest;
+ private final PgsqlServerDecoder decodePgsqlStartupMessage = this::decodePgsqlStartupMessage;
+ private final PgsqlServerDecoder decodePgsqlCancelRequest = this::decodePgsqlCancelRequest;
+ private final PgsqlServerDecoder decodePgsqlMessageType = this::decodePgsqlMessageType;
+ private final PgsqlServerDecoder decodePgsqlQuery = this::decodePgsqlMessageQuery;
+ private final PgsqlServerDecoder decodePgsqlPayload = this::decodePgsqlMessagePayload;
+ private final PgsqlServerDecoder decodePgsqlTermination = this::decodePgsqlMessageTerminator;
+ private final PgsqlServerDecoder decodePgsqlIgnoreOne = this::decodePgsqlIgnoreOne;
+ private final PgsqlServerDecoder decodePgsqlIgnoreAll = this::decodePgsqlIgnoreAll;
+
+ private final Int2ObjectHashMap decodersByType;
+ {
+ Int2ObjectHashMap decodersByType = new Int2ObjectHashMap<>();
+ decodersByType.put(MESSAGE_TYPE_QUERY, decodePgsqlQuery);
+ decodersByType.put(MESSAGE_TYPE_TERMINATE, decodePgsqlTermination);
+ this.decodersByType = decodersByType;
+ }
+
+ public PgsqlServerFactory(
+ PgsqlConfiguration config,
+ EngineContext context)
+ {
+ this.writeBuffer = requireNonNull(context.writeBuffer());
+ this.messageBuffer = new UnsafeBuffer(new byte[writeBuffer.capacity()]);
+ this.supplyInitialId = context::supplyInitialId;
+ this.supplyReplyId = context::supplyReplyId;
+ this.streamFactory = context.streamFactory();
+ this.bufferPool = context.bufferPool();
+ this.decodeMax = bufferPool.slotCapacity();
+
+ this.bindings = new Long2ObjectHashMap<>();
+
+ this.pgsqlTypeId = context.supplyTypeId(PgsqlBinding.NAME);
+ }
+
+ @Override
+ public void attach(
+ BindingConfig binding)
+ {
+ PgsqlBindingConfig pgsqlBinding = new PgsqlBindingConfig(binding);
+ bindings.put(binding.id, pgsqlBinding);
+ }
+
+ @Override
+ public void detach(
+ long bindingId)
+ {
+ bindings.remove(bindingId);
+ }
+
+ @Override
+ public MessageConsumer newStream(
+ int msgTypeId,
+ DirectBuffer buffer,
+ int index,
+ int length,
+ MessageConsumer network)
+ {
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ final long originId = begin.originId();
+ final long routedId = begin.routedId();
+ final long initialId = begin.streamId();
+ final long affinity = begin.affinity();
+ final long authorization = begin.authorization();
+
+ PgsqlBindingConfig binding = bindings.get(routedId);
+
+ MessageConsumer newStream = null;
+
+ if (binding != null)
+ {
+ PgsqlRouteConfig route = binding.resolve(authorization);
+
+ if (route != null)
+ {
+ newStream = new PgsqlServer(
+ network,
+ originId,
+ routedId,
+ initialId,
+ affinity,
+ route.id)::onNetworkMessage;
+ }
+ }
+
+ return newStream;
+ }
+
+ private final class PgsqlServer
+ {
+ private final PgsqlStream stream;
+ private PgsqlServerDecoder decoder;
+
+ private final MessageConsumer network;
+ private final long originId;
+ private final long routedId;
+ private long authorization;
+
+ private final long initialId;
+ private final long replyId;
+ private final long affinity;
+
+ private long initialSeq;
+ private long initialAck;
+ private int initialMax;
+ private int initialPadding;
+
+ private long replySeq;
+ private long replyAck;
+ private int replyMax;
+ private int replyPad;
+ private long replyBudgetId;
+
+ private int decodeSlot = NO_SLOT;
+ private int decodeSlotOffset;
+ private int decodeSlotReserved;
+
+ private int state;
+
+ private PgsqlServer(
+ MessageConsumer network,
+ long originId,
+ long routedId,
+ long initialId,
+ long affinity,
+ long resolvedId)
+ {
+ this.network = network;
+ this.originId = originId;
+ this.routedId = routedId;
+ this.initialId = initialId;
+ this.replyId = supplyReplyId.applyAsLong(initialId);
+ this.affinity = affinity;
+ this.initialMax = decodeMax;
+
+ this.stream = new PgsqlStream(this, routedId, resolvedId);
+ this.decoder = decodePgsqlInitial;
+ }
+
+ private void onNetworkMessage(
+ final int msgTypeId,
+ final DirectBuffer buffer,
+ final int index,
+ final int length)
+ {
+ switch (msgTypeId)
+ {
+ case BeginFW.TYPE_ID:
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ onNetworkBegin(begin);
+ break;
+ case DataFW.TYPE_ID:
+ final DataFW data = dataRO.wrap(buffer, index, index + length);
+ onNetworkData(data);
+ break;
+ case EndFW.TYPE_ID:
+ final EndFW end = endRO.wrap(buffer, index, index + length);
+ onNetworkEnd(end);
+ break;
+ case AbortFW.TYPE_ID:
+ final AbortFW abort = abortRO.wrap(buffer, index, index + length);
+ onNetworkAbort(abort);
+ break;
+ case ResetFW.TYPE_ID:
+ final ResetFW reset = resetRO.wrap(buffer, index, index + length);
+ onNetworkReset(reset);
+ break;
+ case WindowFW.TYPE_ID:
+ final WindowFW window = windowRO.wrap(buffer, index, index + length);
+ onNetworkWindow(window);
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ private void onNetworkBegin(
+ final BeginFW begin)
+ {
+ final long sequence = begin.sequence();
+ final long acknowledge = begin.acknowledge();
+ final int maximum = begin.maximum();
+ final long traceId = begin.traceId();
+ final long authorization = begin.authorization();
+ final long affinity = begin.affinity();
+
+ assert acknowledge == sequence;
+ assert sequence >= initialSeq;
+ assert maximum == 0;
+
+ initialSeq = sequence;
+ initialAck = acknowledge;
+
+ state = PgsqlState.openingInitial(state);
+
+ doNetworkWindow(traceId, authorization, 0L, 0, 0);
+ doNetworkBegin(traceId, authorization, affinity);
+ }
+
+ private void onNetworkData(
+ final DataFW data)
+ {
+ final long sequence = data.sequence();
+ final long acknowledge = data.acknowledge();
+ final long traceId = data.traceId();
+ final long budgetId = data.budgetId();
+ authorization = data.authorization();
+
+ assert acknowledge <= sequence;
+ assert sequence >= initialSeq;
+ assert acknowledge <= initialAck;
+
+ initialSeq = sequence + data.reserved();
+
+ assert initialAck <= initialSeq;
+
+ if (initialSeq > initialAck + decodeMax)
+ {
+ cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final OctetsFW payload = data.payload();
+ DirectBuffer buffer = payload.buffer();
+ int offset = payload.offset();
+ int limit = payload.limit();
+ int reserved = data.reserved();
+
+ if (decodeSlot != NO_SLOT)
+ {
+ final MutableDirectBuffer slotBuffer = bufferPool.buffer(decodeSlot);
+ slotBuffer.putBytes(decodeSlotOffset, buffer, offset, limit - offset);
+ decodeSlotOffset += limit - offset;
+ decodeSlotReserved += reserved;
+
+ buffer = slotBuffer;
+ offset = 0;
+ limit = decodeSlotOffset;
+ reserved = decodeSlotReserved;
+ }
+
+ decodeNetwork(traceId, authorization, budgetId, reserved, buffer, offset, limit);
+ }
+ }
+
+ private void onNetworkEnd(
+ final EndFW end)
+ {
+ final long traceId = end.traceId();
+ final long authorization = end.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ cleanupDecodeSlotIfNecessary();
+
+ stream.doApplicationEnd(traceId, authorization);
+ }
+
+ private void onNetworkAbort(
+ final AbortFW abort)
+ {
+ final long traceId = abort.traceId();
+ final long authorization = abort.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ stream.doApplicationAbort(traceId, authorization);
+ }
+
+ private void onNetworkReset(
+ final ResetFW reset)
+ {
+ final long traceId = reset.traceId();
+ final long authorization = reset.authorization();
+
+ state = PgsqlState.closeReply(state);
+
+ stream.doApplicationReset(traceId, authorization);
+ }
+
+ private void onNetworkWindow(
+ final WindowFW window)
+ {
+ final long sequence = window.sequence();
+ final long acknowledge = window.acknowledge();
+ final long traceId = window.traceId();
+ final long authorization = window.authorization();
+ final long budgetId = window.budgetId();
+ final int maximum = window.maximum();
+ final int padding = window.padding();
+
+ assert acknowledge <= sequence;
+ assert sequence <= replySeq;
+ assert acknowledge >= replyAck;
+ assert maximum >= replyMax;
+
+ replyBudgetId = budgetId;
+ replyAck = acknowledge;
+ replyMax = maximum;
+ replyPad = padding;
+
+ assert replyAck <= replySeq;
+
+ state = PgsqlState.openReply(state);
+
+ stream.doApplicationWindow(traceId, authorization, budgetId, (int)(replySeq - replyAck), replyPad);
+ }
+
+ private void doNetworkBegin(
+ long traceId,
+ long authorization,
+ long affinity)
+ {
+ doBegin(network, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, affinity, EMPTY_OCTETS);
+
+ state = PgsqlState.openingReply(state);
+ }
+
+ private void doNetworkData(
+ long traceId,
+ long authorization,
+ int flags,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int length)
+ {
+ final int reserved = length + replyPad;
+
+ doData(network, originId, routedId, replyId, replySeq, replyAck, replyMax, traceId, authorization,
+ flags, budgetId, reserved, buffer, offset, length, EMPTY_EXTENSION);
+
+ replySeq += reserved;
+ }
+
+ private void doNetworkWindow(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int pendingAck,
+ int paddingMin)
+ {
+ final long initialAckMax = Math.max(initialSeq - pendingAck, initialAck);
+
+ if (initialAckMax > initialAck ||
+ stream.initialMax > initialMax ||
+ initialMax > stream.initialMax)
+ {
+ initialAck = initialAckMax;
+ initialMax = Math.max(stream.initialMax, initialMax);
+ initialPadding = paddingMin;
+ assert initialAck <= initialSeq;
+
+ state = PgsqlState.openInitial(state);
+
+ doWindow(network, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, budgetId, initialPadding);
+ }
+ }
+
+ private void doNetworkResetAndAbort(
+ long traceId,
+ long authorization)
+ {
+ doNetworkReset(traceId, authorization);
+ doNetworkAbort(traceId, authorization);
+ }
+
+ private void doNetworkEnd(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeReply(state);
+
+ doEnd(network, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doNetworkAbort(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeReply(state);
+
+ doAbort(network, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doNetworkReset(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closingInitial(state);
+
+ doReset(network, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+
+ cleanupDecodeSlotIfNecessary();
+ }
+
+ private int decodeNetwork(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int reserved,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ PgsqlServerDecoder previous = null;
+ int progress = offset;
+ while (progress <= limit && previous != decoder)
+ {
+ previous = decoder;
+ progress = decoder.decode(this, traceId, authorization, budgetId, buffer, progress, limit);
+ }
+
+ if (progress < limit)
+ {
+ if (decodeSlot == NO_SLOT)
+ {
+ decodeSlot = bufferPool.acquire(initialId);
+ }
+
+ if (decodeSlot == NO_SLOT)
+ {
+ cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final MutableDirectBuffer decodeBuffer = bufferPool.buffer(decodeSlot);
+ decodeBuffer.putBytes(0, buffer, progress, limit - progress);
+ decodeSlotOffset = limit - progress;
+ decodeSlotReserved = (int)((long) reserved * (limit - progress) / (limit - offset));
+ }
+ }
+ else
+ {
+ cleanupDecodeSlotIfNecessary();
+ }
+
+ if (!PgsqlState.initialClosed(state))
+ {
+ doNetworkWindow(traceId, authorization, budgetId, decodeSlotReserved, initialPadding);
+ }
+
+ return progress;
+ }
+
+ public void onDecodeSslRequest(
+ long traceId,
+ long authorization)
+ {
+ PgsqlSslResponseFW sslResponse = sslResponseRW.wrap(messageBuffer, 0, messageBuffer.capacity()).build();
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, sslResponse.limit());
+ }
+
+ private void onDecodeStartup(
+ long traceId,
+ long authorization,
+ Array32FW parameters)
+ {
+ Consumer beginEx = e -> e.set((b, o, l) -> beginExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .parameters(parameters)
+ .build().sizeof());
+ stream.doApplicationBegin(traceId, authorization, beginEx);
+
+ doNetworkWindow(traceId, authorization, authorization, decodeSlotReserved, initialPadding);
+
+ PgsqlAuthenticationMessageFW authMessage = authMessageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .build();
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, authMessage.limit());
+
+ PgsqlBackendKeyMessageFW backendKeyMessage =
+ backendKeyMessageRW.wrap(messageBuffer, 0, messageBuffer.capacity()).build();
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, backendKeyMessage.limit());
+
+ doEncodeParamStatus(traceId, "client_encoding", "UTF8");
+ doEncodeParamStatus(traceId, "standard_conforming_strings", "on");
+ doEncodeParamStatus(traceId, "server_version", "1.0.0");
+ doEncodeParamStatus(traceId, "application_name", "zilla");
+
+ int progress = 0;
+ PgsqlMessageFW message = messageRW.wrap(messageBuffer, progress, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_READY)
+ .length(Integer.BYTES + Byte.BYTES)
+ .build();
+ progress = message.limit();
+
+ messageBuffer.putByte(progress, (byte) PgsqlStatus.IDLE.value());
+ progress += Byte.BYTES;
+
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, progress);
+ }
+
+ private void doEncodeParamStatus(
+ long traceId,
+ String name,
+ String value)
+ {
+ int statusOffset = 0;
+
+ PgsqlMessageFW status = messageRW.wrap(messageBuffer, statusOffset, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_PARAMETER_STATUS)
+ .length(0)
+ .build();
+ statusOffset = status.limit();
+
+ messageBuffer.putBytes(statusOffset, name.getBytes());
+ statusOffset += name.length();
+ messageBuffer.putByte(statusOffset, (byte) END_OF_FIELD);
+ statusOffset += Byte.BYTES;
+
+ messageBuffer.putBytes(statusOffset, value.getBytes());
+ statusOffset += value.length();
+ messageBuffer.putByte(statusOffset, (byte) END_OF_FIELD);
+ statusOffset += Byte.BYTES;
+
+ messageBuffer.putInt(Byte.BYTES, statusOffset - Byte.BYTES, BIG_ENDIAN);
+
+ doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, statusOffset);
+ }
+
+ private void onDecodeMessageQuery(
+ long traceId,
+ long authorization,
+ int flags,
+ int deferred,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ Consumer queryEx = e -> e.set((b, o, l) -> dataExRW.wrap(b, o, l)
+ .typeId(pgsqlTypeId)
+ .query(q -> q.deferred(deferred))
+ .build().sizeof());
+
+ stream.doApplicationData(traceId, authorization, flags, buffer, offset, limit, queryEx);
+ }
+
+ private void onDecodeMessageTermination(
+ long traceId,
+ long authorization)
+ {
+ stream.doApplicationEnd(traceId, authorization);
+ doNetworkEnd(traceId, authorization);
+ }
+
+ private void cleanupNetwork(
+ long traceId,
+ long authorization)
+ {
+ cleanup(traceId, authorization, this::doNetworkResetAndAbort);
+ }
+
+ private void cleanup(
+ long traceId,
+ long authorization,
+ LongLongConsumer cleanupHandler)
+ {
+ cleanupHandler.accept(traceId, authorization);
+ stream.doApplicationAbortAndReset(traceId, authorization);
+ }
+
+ private void cleanupDecodeSlotIfNecessary()
+ {
+ if (decodeSlot != NO_SLOT)
+ {
+ bufferPool.release(decodeSlot);
+ decodeSlot = NO_SLOT;
+ decodeSlotOffset = 0;
+ decodeSlotReserved = 0;
+ }
+ }
+ }
+
+ private final class PgsqlStream
+ {
+ private final PgsqlServer server;
+
+ private MessageConsumer application;
+
+ private final long initialId;
+ private final long replyId;
+ private final long originId;
+ private final long routedId;
+
+ private long initialSeq;
+ private long initialAck;
+ private int initialMax;
+ private int initialPad;
+ private long initialBudgetId;
+
+ private long replySeq;
+ private long replyAck;
+ private int replyMax;
+
+ private int state;
+
+ private PgsqlStream(
+ PgsqlServer server,
+ long originId,
+ long routedId)
+ {
+ this.server = server;
+ this.originId = originId;
+ this.routedId = routedId;
+ this.initialId = supplyInitialId.applyAsLong(routedId);
+ this.replyId = supplyReplyId.applyAsLong(initialId);
+ }
+
+ private void onApplicationMessage(
+ final int msgTypeId,
+ final DirectBuffer buffer,
+ final int index,
+ final int length)
+ {
+ switch (msgTypeId)
+ {
+ case BeginFW.TYPE_ID:
+ final BeginFW begin = beginRO.wrap(buffer, index, index + length);
+ onApplicationBegin(begin);
+ break;
+ case DataFW.TYPE_ID:
+ final DataFW data = dataRO.wrap(buffer, index, index + length);
+ onApplicationData(data);
+ break;
+ case EndFW.TYPE_ID:
+ final EndFW end = endRO.wrap(buffer, index, index + length);
+ onApplicationEnd(end);
+ break;
+ case AbortFW.TYPE_ID:
+ final AbortFW abort = abortRO.wrap(buffer, index, index + length);
+ onApplicationAbort(abort);
+ break;
+ case FlushFW.TYPE_ID:
+ final FlushFW flush = flushRO.wrap(buffer, index, index + length);
+ onApplicationFlush(flush);
+ break;
+ case ResetFW.TYPE_ID:
+ final ResetFW reset = resetRO.wrap(buffer, index, index + length);
+ onApplicationReset(reset);
+ break;
+ case WindowFW.TYPE_ID:
+ final WindowFW window = windowRO.wrap(buffer, index, index + length);
+ onApplicationWindow(window);
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ private void onApplicationBegin(
+ final BeginFW begin)
+ {
+ final long traceId = begin.traceId();
+ final long authorization = begin.authorization();
+
+ state = PgsqlState.openingReply(state);
+
+ doApplicationWindow(traceId, authorization, server.replyBudgetId, 0, 0);
+ }
+
+ private void onApplicationData(
+ final DataFW data)
+ {
+ final long sequence = data.sequence();
+ final long acknowledge = data.acknowledge();
+ final long traceId = data.traceId();
+ final long authorization = data.authorization();
+ final long budgetId = data.budgetId();
+ final int reserved = data.reserved();
+
+ assert acknowledge <= sequence;
+ assert sequence >= replySeq;
+ assert acknowledge <= replyAck;
+ assert budgetId == server.replyBudgetId;
+
+ replySeq = sequence + reserved;
+
+ assert replyAck <= replySeq;
+
+ if (replySeq > replyAck + replyMax)
+ {
+ server.cleanupNetwork(traceId, authorization);
+ }
+ else
+ {
+ final OctetsFW extension = data.extension();
+ final ExtensionFW dataEx = extension.get(extensionRO::tryWrap);
+
+ final PgsqlDataExFW pgsqlDataEx = dataEx != null && dataEx.typeId() == pgsqlTypeId ?
+ extension.get(pgsqlDataExRO::tryWrap) : null;
+
+ if (pgsqlDataEx.kind() == PgsqlDataExFW.KIND_ROW)
+ {
+ final OctetsFW payload = data.payload();
+
+ if (payload != null)
+ {
+ doEncodeRow(traceId, authorization, payload);
+ }
+ }
+ }
+ }
+
+ private void onApplicationFlush(
+ final FlushFW flush)
+ {
+ final long sequence = flush.sequence();
+ final long acknowledge = flush.acknowledge();
+ final long traceId = flush.traceId();
+ final long authorization = flush.authorization();
+ final int reserved = flush.reserved();
+
+ assert acknowledge <= sequence;
+ assert sequence >= replySeq;
+ assert acknowledge <= replyAck;
+
+ replySeq = sequence;
+
+ assert replyAck <= replySeq;
+
+ if (replySeq > replySeq + replyMax)
+ {
+ server.cleanupNetwork(traceId, authorization);
+ }
+
+ final OctetsFW extension = flush.extension();
+ final ExtensionFW flushEx = extension.get(extensionRO::tryWrap);
+
+ final PgsqlFlushExFW pgsqlFlushEx = flushEx != null && flushEx.typeId() == pgsqlTypeId ?
+ extension.get(pgsqlFlushExRO::tryWrap) : null;
+
+ assert pgsqlFlushEx != null;
+
+ switch (pgsqlFlushEx.kind())
+ {
+ case PgsqlFlushExFW.KIND_TYPE:
+ doEncodeType(traceId, authorization, pgsqlFlushEx.type());
+ break;
+ case PgsqlFlushExFW.KIND_COMPLETION:
+ doEncodeCompletion(traceId, authorization, pgsqlFlushEx.completion());
+ break;
+ case PgsqlFlushExFW.KIND_READY:
+ doEncodeReady(traceId, authorization, pgsqlFlushEx.ready());
+ break;
+ default:
+ assert false;
+ break;
+ }
+ }
+
+ private void onApplicationEnd(
+ final EndFW end)
+ {
+ final long traceId = end.traceId();
+ final long authorization = end.authorization();
+
+ if (!PgsqlState.closed(server.state))
+ {
+ doEncodeTerminate(traceId, authorization);
+
+ state = PgsqlState.closeReply(state);
+
+ server.doNetworkEnd(traceId, authorization);
+ }
+ }
+
+ private void onApplicationAbort(
+ final AbortFW abort)
+ {
+ final long traceId = abort.traceId();
+ final long authorization = abort.authorization();
+
+ state = PgsqlState.closeReply(state);
+
+ server.doNetworkAbort(traceId, authorization);
+ }
+
+ private void onApplicationReset(
+ final ResetFW reset)
+ {
+ final long traceId = reset.traceId();
+ final long authorization = reset.authorization();
+
+ state = PgsqlState.closeInitial(state);
+
+ server.doNetworkReset(traceId, authorization);
+ }
+
+ private void onApplicationWindow(
+ final WindowFW window)
+ {
+ final long sequence = window.sequence();
+ final long acknowledge = window.acknowledge();
+ final int maximum = window.maximum();
+ final long traceId = window.traceId();
+ final long authorization = window.authorization();
+ final long budgetId = window.budgetId();
+ final int padding = window.padding();
+
+ assert acknowledge <= sequence;
+ assert acknowledge >= initialAck;
+ assert maximum + acknowledge >= initialMax + initialAck;
+
+ initialBudgetId = budgetId;
+ initialAck = acknowledge;
+ initialMax = maximum;
+ initialPad = padding;
+
+ assert initialAck <= initialSeq;
+
+ server.doNetworkWindow(traceId, authorization, budgetId, (int)(initialSeq - initialAck), initialPad);
+ }
+
+ private void doApplicationBegin(
+ long traceId,
+ long authorization,
+ Consumer extension)
+ {
+ final BeginFW begin = beginRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(initialId)
+ .sequence(initialSeq)
+ .acknowledge(initialAck)
+ .maximum(initialMax)
+ .traceId(traceId)
+ .authorization(authorization)
+ .affinity(server.affinity)
+ .extension(extension)
+ .build();
+
+ application = streamFactory.newStream(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof(),
+ this::onApplicationMessage);
+
+ application.accept(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof());
+ }
+
+ private void doApplicationData(
+ long traceId,
+ long authorization,
+ int flags,
+ DirectBuffer buffer,
+ int offset,
+ int limit,
+ Consumer extension)
+ {
+ final int length = limit - offset;
+ final int reserved = length + initialPad;
+
+ doData(application, originId, routedId, initialId, initialSeq, initialAck, initialMax, traceId, authorization,
+ flags, initialBudgetId, reserved, buffer, offset, length, extension);
+
+ initialSeq += reserved;
+ assert initialSeq <= initialAck + initialMax;
+ }
+
+ private void doApplicationEnd(
+ long traceId,
+ long authorization)
+ {
+ doEnd(application, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+
+ state = PgsqlState.closeInitial(state);
+ }
+
+ private void doApplicationAbort(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeInitial(state);
+
+ doAbort(application, originId, routedId, initialId, initialSeq, initialAck, initialMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doApplicationReset(
+ long traceId,
+ long authorization)
+ {
+ state = PgsqlState.closeReply(state);
+
+ doReset(application, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, EMPTY_OCTETS);
+ }
+
+ private void doApplicationAbortAndReset(
+ long traceId,
+ long authorization)
+ {
+ doApplicationAbort(traceId, authorization);
+ doApplicationReset(traceId, authorization);
+ }
+
+ private void doApplicationWindow(
+ long traceId,
+ long authorization,
+ long budgetId,
+ int pendingAck,
+ int paddingMin)
+ {
+ final long replyAckMax = Math.max(replySeq - pendingAck, replyAck);
+ if (PgsqlState.replyOpening(state) &&
+ (replyAckMax > replyAck || server.replyMax > replyMax))
+ {
+ replyAck = replyAckMax;
+ replyMax = server.replyMax;
+ assert replyAck <= replySeq;
+
+ state = PgsqlState.openReply(state);
+
+ doWindow(application, originId, routedId, replyId, replySeq, replyAck, replyMax,
+ traceId, authorization, budgetId, paddingMin);
+ }
+ }
+
+ private void doEncodeType(
+ long traceId,
+ long authorization,
+ PgsqlTypeFlushExFW type)
+ {
+ final MutableInteger typeOffset = new MutableInteger(0);
+
+ PgsqlMessageFW messageType = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_TYPE)
+ .length(0)
+ .build();
+ typeOffset.getAndAdd(messageType.limit());
+
+ messageBuffer.putShort(typeOffset.value, (short) type.columns().fieldCount(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Short.BYTES);
+
+ type.columns().forEach(c ->
+ {
+ final DirectBuffer nameBuffer = c.name().value();
+ final int nameSize = nameBuffer.capacity();
+
+ messageBuffer.putBytes(typeOffset.value, nameBuffer, 0, nameSize);
+ typeOffset.getAndAdd(nameSize);
+ messageBuffer.putInt(typeOffset.value, c.tableOid(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Integer.BYTES);
+ messageBuffer.putShort(typeOffset.value, c.index(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Short.BYTES);
+ messageBuffer.putInt(typeOffset.value, c.typeOid(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Integer.BYTES);
+ messageBuffer.putShort(typeOffset.value, c.length(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Short.BYTES);
+ messageBuffer.putInt(typeOffset.value, c.modifier(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Integer.BYTES);
+ messageBuffer.putShort(typeOffset.value, (short) c.format().get().value(), BIG_ENDIAN);
+ typeOffset.getAndAdd(Short.BYTES);
+ });
+
+ messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_TYPE)
+ .length(typeOffset.get() - Byte.BYTES)
+ .build();
+
+ server.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, typeOffset.value);
+ }
+
+ private void doEncodeRow(
+ long traceId,
+ long authorization,
+ OctetsFW row)
+ {
+ final DirectBuffer rowBuffer = row.value();
+ final int rowSize = rowBuffer.capacity();
+
+ int rowOffset = 0;
+
+ PgsqlMessageFW messageRow = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_DATA_ROW)
+ .length(rowSize + Integer.BYTES)
+ .build();
+ rowOffset += messageRow.limit();
+
+ messageBuffer.putBytes(rowOffset, rowBuffer, 0, rowSize);
+ rowOffset += rowSize;
+
+ server.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, rowOffset);
+ }
+
+ private void doEncodeCompletion(
+ long traceId,
+ long authorization,
+ PgsqlCompletedFlushExFW completion)
+ {
+ final DirectBuffer tagBuffer = completion.tag().value();
+ final int tagSize = tagBuffer.capacity();
+
+ int completionOffset = 0;
+
+ PgsqlMessageFW messageCompleted = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_COMPLETION)
+ .length(Integer.BYTES + tagSize)
+ .build();
+ completionOffset += messageCompleted.limit();
+
+ messageBuffer.putBytes(completionOffset, tagBuffer, 0, tagSize);
+ completionOffset += tagSize;
+
+ server.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, completionOffset);
+ }
+
+ private void doEncodeReady(
+ long traceId,
+ long authorization,
+ PgsqlReadyFlushExFW ready)
+ {
+ int readyOffset = 0;
+
+ PgsqlMessageFW messageReady = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_READY)
+ .length(ready.status().sizeof() + Integer.BYTES)
+ .build();
+ readyOffset += messageReady.limit();
+
+ messageBuffer.putByte(messageReady.limit(), (byte) ready.status().get().value());
+ readyOffset += Byte.BYTES;
+
+ server.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, readyOffset);
+ }
+
+ private void doEncodeTerminate(
+ long traceId,
+ long authorization)
+ {
+ PgsqlMessageFW messageReady = messageRW.wrap(messageBuffer, 0, messageBuffer.capacity())
+ .type(MESSAGE_TYPE_TERMINATE)
+ .length(Integer.BYTES)
+ .build();
+
+ server.doNetworkData(traceId, authorization, FLAGS_COMP, 0L, messageBuffer, 0, messageReady.limit());
+ }
+ }
+
+ private void doBegin(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final long affinity,
+ final OctetsFW extension)
+ {
+ final BeginFW begin = beginRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .affinity(affinity)
+ .extension(extension)
+ .build();
+
+ receiver.accept(begin.typeId(), begin.buffer(), begin.offset(), begin.sizeof());
+ }
+
+ private void doData(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final int flags,
+ final long budgetId,
+ final int reserved,
+ DirectBuffer buffer,
+ int offset,
+ int length,
+ Consumer extension)
+ {
+ final DataFW data = dataRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .flags(flags)
+ .budgetId(budgetId)
+ .reserved(reserved)
+ .payload(buffer, offset, length)
+ .extension(extension)
+ .build();
+
+ receiver.accept(data.typeId(), data.buffer(), data.offset(), data.sizeof());
+ }
+
+ private void doAbort(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final AbortFW abort = abortRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ receiver.accept(abort.typeId(), abort.buffer(), abort.offset(), abort.sizeof());
+ }
+
+ private void doEnd(
+ final MessageConsumer receiver,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final EndFW end = endRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ receiver.accept(end.typeId(), end.buffer(), end.offset(), end.sizeof());
+ }
+
+ private void doReset(
+ final MessageConsumer sender,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ final long authorization,
+ final OctetsFW extension)
+ {
+ final ResetFW reset = resetRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .extension(extension)
+ .build();
+
+ sender.accept(reset.typeId(), reset.buffer(), reset.offset(), reset.sizeof());
+ }
+
+ private void doWindow(
+ final MessageConsumer sender,
+ final long originId,
+ final long routedId,
+ final long streamId,
+ final long sequence,
+ final long acknowledge,
+ final int maximum,
+ final long traceId,
+ long authorization,
+ final long budgetId,
+ final int padding)
+ {
+ final WindowFW window = windowRW.wrap(writeBuffer, 0, writeBuffer.capacity())
+ .originId(originId)
+ .routedId(routedId)
+ .streamId(streamId)
+ .sequence(sequence)
+ .acknowledge(acknowledge)
+ .maximum(maximum)
+ .traceId(traceId)
+ .authorization(authorization)
+ .budgetId(budgetId)
+ .padding(padding)
+ .build();
+
+ sender.accept(window.typeId(), window.buffer(), window.offset(), window.sizeof());
+ }
+
+ private int decodePgsqlInitial(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ final PgsqlSslRequestFW pgsqlSslRequest = sslRequestRO.tryWrap(buffer, offset, limit);
+ final PgsqlCancelRequestMessageFW cancelRequest = cancelReqMessageRO.tryWrap(buffer, offset, limit);
+ final PgsqlStartupMessageFW startupMessage = startupMessageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlSslRequest != null &&
+ pgsqlSslRequest.code() == SSL_REQUEST_CODE)
+ {
+ server.decoder = decodePgsqlSslRequest;
+ }
+ else if (cancelRequest != null &&
+ cancelRequest.code() == CANCEL_REQUEST_CODE)
+ {
+ server.decoder = decodePgsqlCancelRequest;
+ }
+ else if (startupMessage != null)
+ {
+ server.decoder = decodePgsqlStartupMessage;
+ }
+ else
+ {
+ server.decoder = decodePgsqlIgnoreAll;
+ }
+
+ return offset;
+ }
+
+ private int decodePgsqlSslRequest(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ PgsqlSslRequestFW sslRequest = sslRequestRO.wrap(buffer, offset, limit);
+
+ server.onDecodeSslRequest(traceId, authorization);
+ server.decoder = decodePgsqlStartupMessage;
+
+ return sslRequest.limit();
+ }
+
+ private int decodePgsqlStartupMessage(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ progress.set(offset);
+
+ PgsqlStartupMessageFW startupMessage = startupMessageRO.tryWrap(buffer, offset, limit);
+
+ if (startupMessage != null)
+ {
+ progress.set(startupMessage.limit());
+ Array32FW.Builder parameterBuilder =
+ parametersRW.wrap(messageBuffer, 0, messageBuffer.capacity());
+ final int maxLimit = startupMessage.length() + progress.value - startupMessage.sizeof() - Integer.BYTES;
+ while (progress.value < maxLimit)
+ {
+ final int nameLength = getLengthOfString(buffer, progress.value);
+ final int valueLength = getLengthOfString(buffer, progress.value + nameLength);
+
+ parameterBuilder.item(i -> i
+ .name(buffer, progress.value, nameLength)
+ .value(buffer, progress.value + nameLength, valueLength));
+
+ progress.addAndGet(nameLength + valueLength);
+
+ if (buffer.getByte(progress.value) == (byte) END_OF_FIELD)
+ {
+ progress.addAndGet(Byte.BYTES);
+ }
+ }
+
+ server.onDecodeStartup(traceId, authorization, parametersRW.build());
+ server.decoder = decodePgsqlMessageType;
+ }
+
+ return progress.value;
+ }
+
+ private int decodePgsqlCancelRequest(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ PgsqlSslRequestFW sslRequest = sslRequestRO.wrap(buffer, offset, limit);
+
+ server.onDecodeSslRequest(traceId, authorization);
+ server.decoder = decodePgsqlStartupMessage;
+
+ return sslRequest.limit();
+ }
+
+ private int decodePgsqlMessageType(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progress = offset;
+
+ final PgsqlMessageFW pgsqlMessage = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlMessage != null)
+ {
+ final int type = pgsqlMessage.type();
+
+ final PgsqlServerDecoder decoder = decodersByType.getOrDefault(type, decodePgsqlIgnoreOne);
+ server.decoder = decoder;
+ }
+
+ return progress;
+ }
+
+ private int decodePgsqlMessageQuery(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlQuery = messageRO.tryWrap(buffer, progressOffset, limit);
+ if (pgsqlQuery != null)
+ {
+ progressOffset = pgsqlQuery.limit();
+
+ final int querySize = pgsqlQuery.length() - Integer.BYTES;
+ payloadRemaining.set(querySize);
+
+ final int length = Math.min(payloadRemaining.value, limit - progressOffset);
+
+ if (length > 0)
+ {
+ final int flags = querySize == length ? FLAGS_COMP : FLAGS_INIT;
+ final int deferred = querySize - length;
+
+ server.onDecodeMessageQuery(traceId, authorization, flags, deferred,
+ buffer, progressOffset, progressOffset + length);
+ progressOffset += length;
+ payloadRemaining.set(querySize - length);
+
+ assert payloadRemaining.get() >= 0;
+
+ server.decoder = querySize == length
+ ? decodePgsqlMessageType
+ : decodePgsqlPayload;
+ }
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlMessagePayload(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ final int payloadSize = payloadRemaining.get();
+ final int length = Math.min(payloadSize, limit - offset);
+
+ final int flags = payloadSize == length ? FLAGS_FIN : FLAGS_CONT;
+
+ final int maxLimit = offset + length;
+ server.stream.doApplicationData(traceId, authorization, flags, buffer, offset, maxLimit, EMPTY_EXTENSION);
+ payloadRemaining.set(payloadSize - length);
+
+ assert payloadRemaining.get() >= 0;
+
+ if (payloadSize == length)
+ {
+ server.decoder = decodePgsqlMessageType;
+ }
+
+ return maxLimit;
+ }
+
+ private int decodePgsqlMessageTerminator(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ int progressOffset = offset;
+
+ final PgsqlMessageFW pgsqlTerminate = messageRO.tryWrap(buffer, offset, limit);
+
+ if (pgsqlTerminate != null)
+ {
+ server.onDecodeMessageTermination(traceId, authorization);
+
+ server.decoder = decodePgsqlIgnoreAll;
+ progressOffset = limit;
+ }
+
+ return progressOffset;
+ }
+
+ private int decodePgsqlIgnoreOne(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ final PgsqlMessageFW messageType = messageRO.wrap(buffer, offset, limit);
+ final int progress = messageType.limit() + messageType.length();
+
+ server.decoder = decodePgsqlMessageType;
+ return progress;
+ }
+
+ private int decodePgsqlIgnoreAll(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit)
+ {
+ return limit;
+ }
+
+ @FunctionalInterface
+ private interface PgsqlServerDecoder
+ {
+ int decode(
+ PgsqlServer server,
+ long traceId,
+ long authorization,
+ long budgetId,
+ DirectBuffer buffer,
+ int offset,
+ int limit);
+ }
+
+ private int getLengthOfString(
+ DirectBuffer buffer,
+ int offset)
+ {
+ int length = -1;
+ loop:
+ for (int progress = offset; progress < buffer.capacity(); progress++)
+ {
+ if (buffer.getByte(progress) == END_OF_FIELD)
+ {
+ length = progress - offset + 1;
+ break loop;
+ }
+ }
+ return length;
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlState.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlState.java
new file mode 100644
index 0000000000..3ce9569e75
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlState.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+public final class PgsqlState
+{
+ private static final int INITIAL_OPENING = 0x10;
+ private static final int INITIAL_OPENED = 0x20;
+ private static final int INITIAL_CLOSING = 0x40;
+ private static final int INITIAL_CLOSED = 0x80;
+ private static final int REPLY_OPENING = 0x01;
+ private static final int REPLY_OPENED = 0x02;
+ private static final int REPLY_CLOSING = 0x04;
+ private static final int REPLY_CLOSED = 0x08;
+
+ static int openingInitial(
+ int state)
+ {
+ return state | INITIAL_OPENING;
+ }
+
+ static int openInitial(
+ int state)
+ {
+ return openingInitial(state) | INITIAL_OPENED;
+ }
+
+ static int closingInitial(
+ int state)
+ {
+ return state | INITIAL_CLOSING;
+ }
+
+ static int closeInitial(
+ int state)
+ {
+ return closingInitial(state) | INITIAL_CLOSED;
+ }
+
+ static boolean initialOpening(
+ int state)
+ {
+ return (state & INITIAL_OPENING) != 0;
+ }
+
+ static boolean initialOpened(
+ int state)
+ {
+ return (state & INITIAL_OPENED) != 0;
+ }
+
+ static boolean initialClosing(
+ int state)
+ {
+ return (state & INITIAL_CLOSING) != 0;
+ }
+
+ static boolean initialClosed(
+ int state)
+ {
+ return (state & INITIAL_CLOSED) != 0;
+ }
+
+ static boolean closed(
+ int state)
+ {
+ return initialClosed(state) && replyClosed(state);
+ }
+
+ static int openingReply(
+ int state)
+ {
+ return state | REPLY_OPENING;
+ }
+
+ static int openReply(
+ int state)
+ {
+ return openingReply(state) | REPLY_OPENED;
+ }
+
+ static boolean replyOpening(
+ int state)
+ {
+ return (state & REPLY_OPENING) != 0;
+ }
+
+ static boolean replyOpened(
+ int state)
+ {
+ return (state & REPLY_OPENED) != 0;
+ }
+
+ static int closingReply(
+ int state)
+ {
+ return state | REPLY_CLOSING;
+ }
+
+ static int closeReply(
+ int state)
+ {
+ return closingReply(state) | REPLY_CLOSED;
+ }
+
+ static boolean replyClosed(
+ int state)
+ {
+ return (state & REPLY_CLOSED) != 0;
+ }
+
+ private PgsqlState()
+ {
+ // utility
+ }
+}
diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlStreamFactory.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlStreamFactory.java
new file mode 100644
index 0000000000..dc8005fb2f
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/PgsqlStreamFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+import io.aklivity.zilla.runtime.engine.binding.BindingHandler;
+import io.aklivity.zilla.runtime.engine.config.BindingConfig;
+
+public interface PgsqlStreamFactory extends BindingHandler
+{
+ void attach(
+ BindingConfig binding);
+
+ void detach(
+ long bindingId);
+}
diff --git a/incubator/binding-pgsql/src/main/moditect/module-info.java b/incubator/binding-pgsql/src/main/moditect/module-info.java
new file mode 100644
index 0000000000..3868d46eb3
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/moditect/module-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+module io.aklivity.zilla.runtime.binding.pgsql
+{
+ requires io.aklivity.zilla.runtime.engine;
+
+ provides io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi
+ with io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlBindingFactorySpi;
+}
diff --git a/incubator/binding-pgsql/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi b/incubator/binding-pgsql/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi
new file mode 100644
index 0000000000..0ec23084e3
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi
@@ -0,0 +1 @@
+io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlBindingFactorySpi
diff --git a/incubator/binding-pgsql/src/main/zilla/protocol.idl b/incubator/binding-pgsql/src/main/zilla/protocol.idl
new file mode 100644
index 0000000000..b2e864b06d
--- /dev/null
+++ b/incubator/binding-pgsql/src/main/zilla/protocol.idl
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+scope protocol
+{
+ option byteorder network;
+
+ scope codec
+ {
+ struct PgsqlSslRequest
+ {
+ int32 length = 4;
+ int32 code = 80877103;
+ }
+
+ struct PgsqlSslResponse
+ {
+ uint8 answer = 78;
+ }
+
+ struct PgsqlStartupMessage
+ {
+ int32 length;
+ uint16 majorVersion;
+ uint16 minorVersion;
+ }
+
+ struct PgsqlCancelRequestMessage
+ {
+ int32 length;
+ int32 code = 80877102;
+ int32 pid = 0;
+ int32 key = 0;
+ }
+
+ struct PgsqlAuthenticationMessage
+ {
+ uint8 type = 82;
+ int32 length = 8;
+ int32 authenticationType = 0;
+ }
+
+ struct PgsqlBackendKeyMessage
+ {
+ uint8 type = 75;
+ int32 length = 12;
+ int32 pid = 0;
+ int32 key = 0;
+ }
+
+ struct PgsqlMessage
+ {
+ uint8 type;
+ int32 length;
+ }
+
+ struct PgsqlRowDescription
+ {
+ int32 tableOid;
+ int16 index;
+ int32 typeOid;
+ int16 length;
+ int32 modifier;
+ int16 format;
+ }
+ }
+}
diff --git a/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ClientIT.java b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ClientIT.java
new file mode 100644
index 0000000000..d309166c54
--- /dev/null
+++ b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ClientIT.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_DRAIN_ON_CLOSE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.rules.RuleChain.outerRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+
+import io.aklivity.k3po.runtime.junit.annotation.Specification;
+import io.aklivity.k3po.runtime.junit.rules.K3poRule;
+import io.aklivity.zilla.runtime.engine.test.EngineRule;
+import io.aklivity.zilla.runtime.engine.test.annotation.Configuration;
+
+public class ClientIT
+{
+ private final K3poRule k3po = new K3poRule()
+ .addScriptRoot("net", "io/aklivity/zilla/specs/binding/pgsql/streams/network")
+ .addScriptRoot("app", "io/aklivity/zilla/specs/binding/pgsql/streams/application");
+
+ private final TestRule timeout = new DisableOnDebug(new Timeout(10, SECONDS));
+
+ private final EngineRule engine = new EngineRule()
+ .directory("target/zilla-itests")
+ .countersBufferCapacity(8192)
+ .configure(ENGINE_DRAIN_ON_CLOSE, false)
+ .configurationRoot("io/aklivity/zilla/specs/binding/pgsql/config")
+ .external("net0")
+ .clean();
+
+ @Rule
+ public final TestRule chain = outerRule(engine).around(k3po).around(timeout);
+
+ @Test
+ @Configuration("client.yaml")
+ @Specification({
+ "${app}/create.table.with.primary.key/client",
+ "${net}/create.table.with.primary.key/server" })
+ public void shouldCreateTableWithPrimaryKey() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.yaml")
+ @Specification({
+ "${app}/termination.request/client",
+ "${net}/termination.request/server" })
+ public void shouldHandleTerminationRequest() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.yaml")
+ @Specification({
+ "${app}/select.table/client",
+ "${net}/select.table/server" })
+ public void shouldSelectTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.yaml")
+ @Specification({
+ "${app}/client.sent.write.abort/client",
+ "${net}/client.sent.write.abort/server" })
+ public void shouldHandleClientSentWriteAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.yaml")
+ @Specification({
+ "${app}/client.sent.read.abort/client",
+ "${net}/client.sent.read.abort/server" })
+ public void shouldHandleClientSentReadAbort() throws Exception
+ {
+ k3po.finish();
+ }
+}
diff --git a/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ServerIT.java b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ServerIT.java
new file mode 100644
index 0000000000..6bcfdc2b64
--- /dev/null
+++ b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/internal/stream/ServerIT.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.binding.pgsql.internal.stream;
+
+import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_DRAIN_ON_CLOSE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.rules.RuleChain.outerRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+
+import io.aklivity.k3po.runtime.junit.annotation.Specification;
+import io.aklivity.k3po.runtime.junit.rules.K3poRule;
+import io.aklivity.zilla.runtime.engine.test.EngineRule;
+import io.aklivity.zilla.runtime.engine.test.annotation.Configuration;
+
+public class ServerIT
+{
+ private final K3poRule k3po = new K3poRule()
+ .addScriptRoot("net", "io/aklivity/zilla/specs/binding/pgsql/streams/network")
+ .addScriptRoot("app", "io/aklivity/zilla/specs/binding/pgsql/streams/application");
+
+ private final TestRule timeout = new DisableOnDebug(new Timeout(10, SECONDS));
+
+ private final EngineRule engine = new EngineRule()
+ .directory("target/zilla-itests")
+ .countersBufferCapacity(8192)
+ .configure(ENGINE_DRAIN_ON_CLOSE, false)
+ .configurationRoot("io/aklivity/zilla/specs/binding/pgsql/config")
+ .external("app0")
+ .clean();
+
+ @Rule
+ public final TestRule chain = outerRule(engine).around(k3po).around(timeout);
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/ssl.request/client",
+ "${app}/ssl.request/server" })
+ public void shouldHandleSslRequest() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/termination.request/client",
+ "${app}/termination.request/server" })
+ public void shouldHandleTerminationRequest() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/create.table.with.primary.key/client",
+ "${app}/create.table.with.primary.key/server" })
+ public void shouldCreateTableWithPrimaryKey() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/select.table/client",
+ "${app}/select.table/server" })
+ public void shouldSelectTable() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/client.sent.write.abort/client",
+ "${app}/client.sent.write.abort/server" })
+ public void shouldHandleClientSentWriteAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/client.sent.read.abort/client",
+ "${app}/client.sent.read.abort/server" })
+ public void shouldHandleClientSentReadAbort() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/create.table.fragmented/client",
+ "${app}/create.table.fragmented/server" })
+ public void shouldHandleFragmentedCreateTable() throws Exception
+ {
+ k3po.finish();
+ }
+}
diff --git a/incubator/binding-risingwave.spec/COPYRIGHT b/incubator/binding-risingwave.spec/COPYRIGHT
new file mode 100644
index 0000000000..0cb10b6f62
--- /dev/null
+++ b/incubator/binding-risingwave.spec/COPYRIGHT
@@ -0,0 +1,12 @@
+Copyright ${copyrightYears} Aklivity Inc
+
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
diff --git a/incubator/binding-risingwave.spec/LICENSE b/incubator/binding-risingwave.spec/LICENSE
new file mode 100644
index 0000000000..f6abb6327b
--- /dev/null
+++ b/incubator/binding-risingwave.spec/LICENSE
@@ -0,0 +1,114 @@
+ Aklivity Community License Agreement
+ Version 1.0
+
+This Aklivity Community License Agreement Version 1.0 (the “Agreement”) sets
+forth the terms on which Aklivity, Inc. (“Aklivity”) makes available certain
+software made available by Aklivity under this Agreement (the “Software”). BY
+INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF THE SOFTWARE,
+YOU AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO
+SUCH TERMS AND CONDITIONS, YOU MUST NOT USE THE SOFTWARE. IF YOU ARE RECEIVING
+THE SOFTWARE ON BEHALF OF A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU
+HAVE THE ACTUAL AUTHORITY TO AGREE TO THE TERMS AND CONDITIONS OF THIS
+AGREEMENT ON BEHALF OF SUCH ENTITY. “Licensee” means you, an individual, or
+the entity on whose behalf you are receiving the Software.
+
+ 1. LICENSE GRANT AND CONDITIONS.
+
+ 1.1 License. Subject to the terms and conditions of this Agreement,
+ Aklivity hereby grants to Licensee a non-exclusive, royalty-free,
+ worldwide, non-transferable, non-sublicenseable license during the term
+ of this Agreement to: (a) use the Software; (b) prepare modifications and
+ derivative works of the Software; (c) distribute the Software (including
+ without limitation in source code or object code form); and (d) reproduce
+ copies of the Software (the “License”). Licensee is not granted the
+ right to, and Licensee shall not, exercise the License for an Excluded
+ Purpose. For purposes of this Agreement, “Excluded Purpose” means making
+ available any software-as-a-service, platform-as-a-service,
+ infrastructure-as-a-service or other similar online service that competes
+ with Aklivity products or services that provide the Software.
+
+ 1.2 Conditions. In consideration of the License, Licensee’s distribution
+ of the Software is subject to the following conditions:
+
+ (a) Licensee must cause any Software modified by Licensee to carry
+ prominent notices stating that Licensee modified the Software.
+
+ (b) On each Software copy, Licensee shall reproduce and not remove or
+ alter all Aklivity or third party copyright or other proprietary
+ notices contained in the Software, and Licensee must provide the
+ notice below with each copy.
+
+ “This software is made available by Aklivity, Inc., under the
+ terms of the Aklivity Community License Agreement, Version 1.0
+ located at http://www.Aklivity.io/Aklivity-community-license. BY
+ INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF
+ THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT.”
+
+ 1.3 Licensee Modifications. Licensee may add its own copyright notices
+ to modifications made by Licensee and may provide additional or different
+ license terms and conditions for use, reproduction, or distribution of
+ Licensee’s modifications. While redistributing the Software or
+ modifications thereof, Licensee may choose to offer, for a fee or free of
+ charge, support, warranty, indemnity, or other obligations. Licensee, and
+ not Aklivity, will be responsible for any such obligations.
+
+ 1.4 No Sublicensing. The License does not include the right to
+ sublicense the Software, however, each recipient to which Licensee
+ provides the Software may exercise the Licenses so long as such recipient
+ agrees to the terms and conditions of this Agreement.
+
+ 2. TERM AND TERMINATION. This Agreement will continue unless and until
+ earlier terminated as set forth herein. If Licensee breaches any of its
+ conditions or obligations under this Agreement, this Agreement will
+ terminate automatically and the License will terminate automatically and
+ permanently.
+
+ 3. INTELLECTUAL PROPERTY. As between the parties, Aklivity will retain all
+ right, title, and interest in the Software, and all intellectual property
+ rights therein. Aklivity hereby reserves all rights not expressly granted
+ to Licensee in this Agreement. Aklivity hereby reserves all rights in its
+ trademarks and service marks, and no licenses therein are granted in this
+ Agreement.
+
+ 4. DISCLAIMER. Aklivity HEREBY DISCLAIMS ANY AND ALL WARRANTIES AND
+ CONDITIONS, EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, AND SPECIFICALLY
+ DISCLAIMS ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE, WITH RESPECT TO THE SOFTWARE.
+
+ 5. LIMITATION OF LIABILITY. Aklivity WILL NOT BE LIABLE FOR ANY DAMAGES OF
+ ANY KIND, INCLUDING BUT NOT LIMITED TO, LOST PROFITS OR ANY CONSEQUENTIAL,
+ SPECIAL, INCIDENTAL, INDIRECT, OR DIRECT DAMAGES, HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, ARISING OUT OF THIS AGREEMENT. THE FOREGOING SHALL
+ APPLY TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+
+ 6.GENERAL.
+
+ 6.1 Governing Law. This Agreement will be governed by and interpreted in
+ accordance with the laws of the state of California, without reference to
+ its conflict of laws principles. If Licensee is located within the
+ United States, all disputes arising out of this Agreement are subject to
+ the exclusive jurisdiction of courts located in Santa Clara County,
+ California. USA. If Licensee is located outside of the United States,
+ any dispute, controversy or claim arising out of or relating to this
+ Agreement will be referred to and finally determined by arbitration in
+ accordance with the JAMS International Arbitration Rules. The tribunal
+ will consist of one arbitrator. The place of arbitration will be Palo
+ Alto, California. The language to be used in the arbitral proceedings
+ will be English. Judgment upon the award rendered by the arbitrator may
+ be entered in any court having jurisdiction thereof.
+
+ 6.2 Assignment. Licensee is not authorized to assign its rights under
+ this Agreement to any third party. Aklivity may freely assign its rights
+ under this Agreement to any third party.
+
+ 6.3 Other. This Agreement is the entire agreement between the parties
+ regarding the subject matter hereof. No amendment or modification of
+ this Agreement will be valid or binding upon the parties unless made in
+ writing and signed by the duly authorized representatives of both
+ parties. In the event that any provision, including without limitation
+ any condition, of this Agreement is held to be unenforceable, this
+ Agreement and all licenses and rights granted hereunder will immediately
+ terminate. Waiver by Aklivity of a breach of any provision of this
+ Agreement or the failure by Aklivity to exercise any right hereunder
+ will not be construed as a waiver of any subsequent breach of that right
+ or as a waiver of any other right.
\ No newline at end of file
diff --git a/incubator/binding-risingwave.spec/NOTICE b/incubator/binding-risingwave.spec/NOTICE
new file mode 100644
index 0000000000..f99ca0e278
--- /dev/null
+++ b/incubator/binding-risingwave.spec/NOTICE
@@ -0,0 +1,19 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+ agrona under The Apache License, Version 2.0
+ ICU4J under Unicode/ICU License
+ Jakarta JSON Processing API under Eclipse Public License 2.0 or GNU General Public License, version 2 with the GNU Classpath Exception
+ org.leadpony.justify under The Apache Software License, Version 2.0
+ zilla::incubator::binding-pgsql.spec under Aklivity Community License Agreement
+ zilla::specs::engine.spec under The Apache Software License, Version 2.0
+
diff --git a/incubator/binding-risingwave.spec/NOTICE.template b/incubator/binding-risingwave.spec/NOTICE.template
new file mode 100644
index 0000000000..209ca12f74
--- /dev/null
+++ b/incubator/binding-risingwave.spec/NOTICE.template
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+#GENERATED_NOTICES#
diff --git a/incubator/binding-risingwave.spec/mvnw b/incubator/binding-risingwave.spec/mvnw
new file mode 100755
index 0000000000..d2f0ea3808
--- /dev/null
+++ b/incubator/binding-risingwave.spec/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/incubator/binding-risingwave.spec/mvnw.cmd b/incubator/binding-risingwave.spec/mvnw.cmd
new file mode 100644
index 0000000000..b26ab24f03
--- /dev/null
+++ b/incubator/binding-risingwave.spec/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/incubator/binding-risingwave.spec/pom.xml b/incubator/binding-risingwave.spec/pom.xml
new file mode 100644
index 0000000000..f3d89ea60d
--- /dev/null
+++ b/incubator/binding-risingwave.spec/pom.xml
@@ -0,0 +1,142 @@
+
+
+
+ 4.0.0
+
+ io.aklivity.zilla
+ incubator
+ 0.9.93
+ ../pom.xml
+
+
+ binding-risingwave.spec
+ zilla::incubator::binding-risingwave.spec
+
+
+
+ Aklivity Community License Agreement
+ https://www.aklivity.io/aklivity-community-license/
+ repo
+
+
+
+
+ 1.00
+ 0
+
+
+
+
+ io.aklivity.k3po
+ lang
+ provided
+
+
+ ${project.groupId}
+ binding-pgsql.spec
+ ${project.version}
+
+
+ junit
+ junit
+ test
+
+
+ io.aklivity.k3po
+ control-junit
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+
+
+
+
+ src/main/resources
+
+
+ src/main/scripts
+
+
+
+
+
+ org.jasig.maven
+ maven-notice-plugin
+
+
+ com.mycila
+ license-maven-plugin
+
+
+ maven-checkstyle-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ io.aklivity.k3po
+ k3po-maven-plugin
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ test-jar
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ io/aklivity/zilla/specs/binding/risingwave/internal/types/**/*.class
+
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ ${jacoco.coverage.ratio}
+
+
+ CLASS
+ MISSEDCOUNT
+ ${jacoco.missed.count}
+
+
+
+
+
+
+
+
+
diff --git a/incubator/binding-risingwave.spec/src/main/moditect/module-info.java b/incubator/binding-risingwave.spec/src/main/moditect/module-info.java
new file mode 100644
index 0000000000..2b879f784c
--- /dev/null
+++ b/incubator/binding-risingwave.spec/src/main/moditect/module-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+open module io.aklivity.zilla.specs.binding.risingwave
+{
+ requires transitive io.aklivity.zilla.specs.engine;
+}
diff --git a/incubator/binding-risingwave/COPYRIGHT b/incubator/binding-risingwave/COPYRIGHT
new file mode 100644
index 0000000000..0cb10b6f62
--- /dev/null
+++ b/incubator/binding-risingwave/COPYRIGHT
@@ -0,0 +1,12 @@
+Copyright ${copyrightYears} Aklivity Inc
+
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
diff --git a/incubator/binding-risingwave/LICENSE b/incubator/binding-risingwave/LICENSE
new file mode 100644
index 0000000000..f6abb6327b
--- /dev/null
+++ b/incubator/binding-risingwave/LICENSE
@@ -0,0 +1,114 @@
+ Aklivity Community License Agreement
+ Version 1.0
+
+This Aklivity Community License Agreement Version 1.0 (the “Agreement”) sets
+forth the terms on which Aklivity, Inc. (“Aklivity”) makes available certain
+software made available by Aklivity under this Agreement (the “Software”). BY
+INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF THE SOFTWARE,
+YOU AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO
+SUCH TERMS AND CONDITIONS, YOU MUST NOT USE THE SOFTWARE. IF YOU ARE RECEIVING
+THE SOFTWARE ON BEHALF OF A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU
+HAVE THE ACTUAL AUTHORITY TO AGREE TO THE TERMS AND CONDITIONS OF THIS
+AGREEMENT ON BEHALF OF SUCH ENTITY. “Licensee” means you, an individual, or
+the entity on whose behalf you are receiving the Software.
+
+ 1. LICENSE GRANT AND CONDITIONS.
+
+ 1.1 License. Subject to the terms and conditions of this Agreement,
+ Aklivity hereby grants to Licensee a non-exclusive, royalty-free,
+ worldwide, non-transferable, non-sublicenseable license during the term
+ of this Agreement to: (a) use the Software; (b) prepare modifications and
+ derivative works of the Software; (c) distribute the Software (including
+ without limitation in source code or object code form); and (d) reproduce
+ copies of the Software (the “License”). Licensee is not granted the
+ right to, and Licensee shall not, exercise the License for an Excluded
+ Purpose. For purposes of this Agreement, “Excluded Purpose” means making
+ available any software-as-a-service, platform-as-a-service,
+ infrastructure-as-a-service or other similar online service that competes
+ with Aklivity products or services that provide the Software.
+
+ 1.2 Conditions. In consideration of the License, Licensee’s distribution
+ of the Software is subject to the following conditions:
+
+ (a) Licensee must cause any Software modified by Licensee to carry
+ prominent notices stating that Licensee modified the Software.
+
+ (b) On each Software copy, Licensee shall reproduce and not remove or
+ alter all Aklivity or third party copyright or other proprietary
+ notices contained in the Software, and Licensee must provide the
+ notice below with each copy.
+
+ “This software is made available by Aklivity, Inc., under the
+ terms of the Aklivity Community License Agreement, Version 1.0
+ located at http://www.Aklivity.io/Aklivity-community-license. BY
+ INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY OF
+ THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT.”
+
+ 1.3 Licensee Modifications. Licensee may add its own copyright notices
+ to modifications made by Licensee and may provide additional or different
+ license terms and conditions for use, reproduction, or distribution of
+ Licensee’s modifications. While redistributing the Software or
+ modifications thereof, Licensee may choose to offer, for a fee or free of
+ charge, support, warranty, indemnity, or other obligations. Licensee, and
+ not Aklivity, will be responsible for any such obligations.
+
+ 1.4 No Sublicensing. The License does not include the right to
+ sublicense the Software, however, each recipient to which Licensee
+ provides the Software may exercise the Licenses so long as such recipient
+ agrees to the terms and conditions of this Agreement.
+
+ 2. TERM AND TERMINATION. This Agreement will continue unless and until
+ earlier terminated as set forth herein. If Licensee breaches any of its
+ conditions or obligations under this Agreement, this Agreement will
+ terminate automatically and the License will terminate automatically and
+ permanently.
+
+ 3. INTELLECTUAL PROPERTY. As between the parties, Aklivity will retain all
+ right, title, and interest in the Software, and all intellectual property
+ rights therein. Aklivity hereby reserves all rights not expressly granted
+ to Licensee in this Agreement. Aklivity hereby reserves all rights in its
+ trademarks and service marks, and no licenses therein are granted in this
+ Agreement.
+
+ 4. DISCLAIMER. Aklivity HEREBY DISCLAIMS ANY AND ALL WARRANTIES AND
+ CONDITIONS, EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, AND SPECIFICALLY
+ DISCLAIMS ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE, WITH RESPECT TO THE SOFTWARE.
+
+ 5. LIMITATION OF LIABILITY. Aklivity WILL NOT BE LIABLE FOR ANY DAMAGES OF
+ ANY KIND, INCLUDING BUT NOT LIMITED TO, LOST PROFITS OR ANY CONSEQUENTIAL,
+ SPECIAL, INCIDENTAL, INDIRECT, OR DIRECT DAMAGES, HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, ARISING OUT OF THIS AGREEMENT. THE FOREGOING SHALL
+ APPLY TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+
+ 6.GENERAL.
+
+ 6.1 Governing Law. This Agreement will be governed by and interpreted in
+ accordance with the laws of the state of California, without reference to
+ its conflict of laws principles. If Licensee is located within the
+ United States, all disputes arising out of this Agreement are subject to
+ the exclusive jurisdiction of courts located in Santa Clara County,
+ California. USA. If Licensee is located outside of the United States,
+ any dispute, controversy or claim arising out of or relating to this
+ Agreement will be referred to and finally determined by arbitration in
+ accordance with the JAMS International Arbitration Rules. The tribunal
+ will consist of one arbitrator. The place of arbitration will be Palo
+ Alto, California. The language to be used in the arbitral proceedings
+ will be English. Judgment upon the award rendered by the arbitrator may
+ be entered in any court having jurisdiction thereof.
+
+ 6.2 Assignment. Licensee is not authorized to assign its rights under
+ this Agreement to any third party. Aklivity may freely assign its rights
+ under this Agreement to any third party.
+
+ 6.3 Other. This Agreement is the entire agreement between the parties
+ regarding the subject matter hereof. No amendment or modification of
+ this Agreement will be valid or binding upon the parties unless made in
+ writing and signed by the duly authorized representatives of both
+ parties. In the event that any provision, including without limitation
+ any condition, of this Agreement is held to be unenforceable, this
+ Agreement and all licenses and rights granted hereunder will immediately
+ terminate. Waiver by Aklivity of a breach of any provision of this
+ Agreement or the failure by Aklivity to exercise any right hereunder
+ will not be construed as a waiver of any subsequent breach of that right
+ or as a waiver of any other right.
\ No newline at end of file
diff --git a/incubator/binding-risingwave/NOTICE b/incubator/binding-risingwave/NOTICE
new file mode 100644
index 0000000000..9024d8926d
--- /dev/null
+++ b/incubator/binding-risingwave/NOTICE
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+
diff --git a/incubator/binding-risingwave/NOTICE.template b/incubator/binding-risingwave/NOTICE.template
new file mode 100644
index 0000000000..209ca12f74
--- /dev/null
+++ b/incubator/binding-risingwave/NOTICE.template
@@ -0,0 +1,13 @@
+Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+This project includes:
+#GENERATED_NOTICES#
diff --git a/incubator/binding-risingwave/mvnw b/incubator/binding-risingwave/mvnw
new file mode 100755
index 0000000000..d2f0ea3808
--- /dev/null
+++ b/incubator/binding-risingwave/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/incubator/binding-risingwave/mvnw.cmd b/incubator/binding-risingwave/mvnw.cmd
new file mode 100644
index 0000000000..b26ab24f03
--- /dev/null
+++ b/incubator/binding-risingwave/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/incubator/binding-risingwave/pom.xml b/incubator/binding-risingwave/pom.xml
new file mode 100644
index 0000000000..629e535e60
--- /dev/null
+++ b/incubator/binding-risingwave/pom.xml
@@ -0,0 +1,233 @@
+
+
+
+ 4.0.0
+
+ io.aklivity.zilla
+ incubator
+ 0.9.93
+ ../pom.xml
+
+
+ binding-risingwave
+ zilla::incubator::binding-risingwave
+
+
+
+ Aklivity Community License Agreement
+ https://www.aklivity.io/aklivity-community-license/
+ repo
+
+
+
+
+ 1.00
+ 0
+
+
+
+
+ ${project.groupId}
+ binding-risingwave.spec
+ ${project.version}
+ provided
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ provided
+
+
+ ${project.groupId}
+ engine
+ test-jar
+ ${project.version}
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ io.aklivity.k3po
+ control-junit
+ test
+
+
+ io.aklivity.k3po
+ lang
+ test
+
+
+ org.openjdk.jmh
+ jmh-core
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ test
+
+
+
+
+
+
+ org.jasig.maven
+ maven-notice-plugin
+
+
+ ${project.groupId}
+ flyweight-maven-plugin
+ ${project.version}
+
+ core pgsql
+ io.aklivity.zilla.runtime.binding.risingwave.internal.types
+
+
+
+
+ generate
+
+
+
+
+
+ com.mycila
+ license-maven-plugin
+
+
+ maven-checkstyle-plugin
+
+
+ maven-dependency-plugin
+
+
+ process-resources
+
+ unpack
+
+
+
+
+ ${project.groupId}
+ binding-risingwave.spec
+
+
+ ^\Qio/aklivity/zilla/specs/binding/risingwave/\E
+ io/aklivity/zilla/runtime/binding/risingwave/internal/
+
+
+
+
+ io/aklivity/zilla/specs/binding/risingwave/schema/risingwave.schema.patch.json
+ ${project.build.directory}/classes
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+ io.aklivity.k3po
+ k3po-maven-plugin
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+ test-jar
+
+
+ ${project.groupId}
+ engine
+ ${project.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ io/aklivity/zilla/runtime/binding/risingwave/internal/types/**/*.class
+
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ ${jacoco.coverage.ratio}
+
+
+ CLASS
+ MISSEDCOUNT
+ ${jacoco.missed.count}
+
+
+
+
+
+
+
+ io.gatling
+ maven-shade-plugin
+
+
+
+ org.agrona:agrona
+ io.aklivity.zilla:engine
+ org.openjdk.jmh:jmh-core
+ net.sf.jopt-simple:jopt-simple
+ org.apache.commons:commons-math3
+ commons-cli:commons-cli
+ com.github.biboudis:jmh-profilers
+
+
+
+
+
+
+
diff --git a/incubator/binding-risingwave/src/main/moditect/module-info.java b/incubator/binding-risingwave/src/main/moditect/module-info.java
new file mode 100644
index 0000000000..bea78da164
--- /dev/null
+++ b/incubator/binding-risingwave/src/main/moditect/module-info.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+module io.aklivity.zilla.runtime.binding.risingwave
+{
+ requires io.aklivity.zilla.runtime.engine;
+
+/*
+ exports io.aklivity.zilla.runtime.binding.risingwave.config;
+
+ provides io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi
+ with io.aklivity.zilla.runtime.binding.risingwave.internal.RisingwaveBindingFactorySpi;
+
+ provides io.aklivity.zilla.runtime.engine.config.OptionsConfigAdapterSpi
+ with io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveOptionsConfigAdapter;
+
+ provides io.aklivity.zilla.runtime.engine.config.ConditionConfigAdapterSpi
+ with io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveConditionConfigAdapter;
+
+ provides io.aklivity.zilla.runtime.engine.event.EventFormatterFactorySpi
+ with io.aklivity.zilla.runtime.binding.risingwave.internal.RisingwaveEventFormatterFactory;
+*/
+}
diff --git a/incubator/command-dump/pom.xml b/incubator/command-dump/pom.xml
index 7feb0e9d62..b180033a91 100644
--- a/incubator/command-dump/pom.xml
+++ b/incubator/command-dump/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
incubator
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/incubator/command-log/NOTICE b/incubator/command-log/NOTICE
index f8254841cf..fc2a97b70c 100644
--- a/incubator/command-log/NOTICE
+++ b/incubator/command-log/NOTICE
@@ -13,7 +13,7 @@ under the License.
This project includes:
agrona under The Apache License, Version 2.0
- Apache Commons CLI under Apache-2.0
+ Apache Commons CLI under Apache License, Version 2.0
ICU4J under Unicode/ICU License
Jackson-annotations under The Apache Software License, Version 2.0
Jackson-core under The Apache Software License, Version 2.0
diff --git a/incubator/command-log/pom.xml b/incubator/command-log/pom.xml
index ca3fbc34bf..b28e2eb36b 100644
--- a/incubator/command-log/pom.xml
+++ b/incubator/command-log/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
incubator
- 0.9.92
+ 0.9.93
../pom.xml
@@ -79,7 +79,6 @@
commons-cli
commons-cli
- 1.7.0
diff --git a/incubator/command-tune/pom.xml b/incubator/command-tune/pom.xml
index 405ede2300..104c16f99c 100644
--- a/incubator/command-tune/pom.xml
+++ b/incubator/command-tune/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
incubator
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/incubator/pom.xml b/incubator/pom.xml
index 41d8e0d3d1..c4b064ed57 100644
--- a/incubator/pom.xml
+++ b/incubator/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
@@ -18,8 +18,12 @@
binding-amqp.spec
+ binding-pgsql.spec
+ binding-risingwave.spec
binding-amqp
+ binding-pgsql
+ binding-risingwave
command-log
command-dump
@@ -33,6 +37,16 @@
binding-amqp
${project.version}
+
+ ${project.groupId}
+ binding-pgsql
+ ${project.version}
+
+
+ ${project.groupId}
+ binding-risingwave
+ ${project.version}
+
${project.groupId}
command-log
diff --git a/manager/pom.xml b/manager/pom.xml
index fca25e18d2..c06887b7d2 100644
--- a/manager/pom.xml
+++ b/manager/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/pom.xml b/pom.xml
index 7b183fe120..34b5865837 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
4.0.0
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
pom
zilla
https://github.com/aklivity/zilla
@@ -425,7 +425,7 @@
maven-surefire-plugin
3.0.0-M5
- @{jacoco.java.option} --add-opens java.base/sun.nio.ch=ALL-UNNAMED
+ @{jacoco.java.option} -Xshare:off --add-opens java.base/sun.nio.ch=ALL-UNNAMED
@@ -438,7 +438,7 @@
maven-failsafe-plugin
3.0.0-M4
- @{jacoco.java.option} --add-opens java.base/sun.nio.ch=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true
+ @{jacoco.java.option} -Xshare:off --add-opens java.base/sun.nio.ch=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true
diff --git a/runtime/binding-asyncapi/pom.xml b/runtime/binding-asyncapi/pom.xml
index ce05e2c2f8..5598c3a377 100644
--- a/runtime/binding-asyncapi/pom.xml
+++ b/runtime/binding-asyncapi/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-asyncapi/src/main/java/io/aklivity/zilla/runtime/binding/asyncapi/internal/config/composite/AsyncapiClientGenerator.java b/runtime/binding-asyncapi/src/main/java/io/aklivity/zilla/runtime/binding/asyncapi/internal/config/composite/AsyncapiClientGenerator.java
index 329fa530b8..b4a8fd0ede 100644
--- a/runtime/binding-asyncapi/src/main/java/io/aklivity/zilla/runtime/binding/asyncapi/internal/config/composite/AsyncapiClientGenerator.java
+++ b/runtime/binding-asyncapi/src/main/java/io/aklivity/zilla/runtime/binding/asyncapi/internal/config/composite/AsyncapiClientGenerator.java
@@ -336,14 +336,17 @@ private KafkaTopicConfigBuilder injectKafkaTopicTransforms(
AsyncapiChannelView channel,
List topics)
{
- Optional topicConfig = topics.stream()
- .filter(t -> t.name.equals(channel.address))
- .findFirst();
- topicConfig.ifPresent(kafkaTopicConfig -> topic
- .transforms()
- .extractKey(kafkaTopicConfig.transforms.extractKey)
- .extractHeaders(kafkaTopicConfig.transforms.extractHeaders)
- .build());
+ if (topics != null)
+ {
+ Optional topicConfig = topics.stream()
+ .filter(t -> t.name.equals(channel.address))
+ .findFirst();
+ topicConfig.ifPresent(kafkaTopicConfig -> topic
+ .transforms()
+ .extractKey(kafkaTopicConfig.transforms.extractKey)
+ .extractHeaders(kafkaTopicConfig.transforms.extractHeaders)
+ .build());
+ }
return topic;
}
diff --git a/runtime/binding-asyncapi/src/main/moditect/module-info.java b/runtime/binding-asyncapi/src/main/moditect/module-info.java
index 9af11d62aa..47a4ed8587 100644
--- a/runtime/binding-asyncapi/src/main/moditect/module-info.java
+++ b/runtime/binding-asyncapi/src/main/moditect/module-info.java
@@ -46,6 +46,7 @@
opens io.aklivity.zilla.runtime.binding.asyncapi.internal.model.bindings.kafka;
opens io.aklivity.zilla.runtime.binding.asyncapi.internal.model.bindings.sse;
opens io.aklivity.zilla.runtime.binding.asyncapi.internal.model.bindings.sse.kafka;
+ opens io.aklivity.zilla.runtime.binding.asyncapi.internal.model.parser;
opens io.aklivity.zilla.runtime.binding.asyncapi.internal.view;
exports io.aklivity.zilla.runtime.binding.asyncapi.config;
diff --git a/runtime/binding-echo/pom.xml b/runtime/binding-echo/pom.xml
index 4ed9644fd2..1dc98a1f84 100644
--- a/runtime/binding-echo/pom.xml
+++ b/runtime/binding-echo/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-fan/pom.xml b/runtime/binding-fan/pom.xml
index 849f022211..d78a6e09d5 100644
--- a/runtime/binding-fan/pom.xml
+++ b/runtime/binding-fan/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-filesystem/pom.xml b/runtime/binding-filesystem/pom.xml
index 4e9578369f..57a901e593 100644
--- a/runtime/binding-filesystem/pom.xml
+++ b/runtime/binding-filesystem/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-grpc-kafka/pom.xml b/runtime/binding-grpc-kafka/pom.xml
index 928aa31548..a7fa8e9740 100644
--- a/runtime/binding-grpc-kafka/pom.xml
+++ b/runtime/binding-grpc-kafka/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaConditionConfigAdapter.java b/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaConditionConfigAdapter.java
index fa829dc196..ed43228c99 100644
--- a/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaConditionConfigAdapter.java
+++ b/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaConditionConfigAdapter.java
@@ -128,6 +128,8 @@ public ConditionConfig adaptFromJson(
textValue = ((JsonString) v).getString();
base64Value = encoder64.encodeToString(textValue.getBytes());
break;
+ default:
+ break;
}
GrpcKafkaMetadataValueConfig metadataValue = new GrpcKafkaMetadataValueConfig(new String16FW(textValue),
diff --git a/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaWithProduceHash.java b/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaWithProduceHash.java
index a3a9e7483a..17d6d2446b 100644
--- a/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaWithProduceHash.java
+++ b/runtime/binding-grpc-kafka/src/main/java/io/aklivity/zilla/runtime/binding/grpc/kafka/internal/config/GrpcKafkaWithProduceHash.java
@@ -66,7 +66,7 @@ public OctetsFW correlationId()
if (digest != null && correlationId != null)
{
- octetsRW.reset();
+ octetsRW.rewrap();
newCorrelationId = octetsRW.put(correlationId).put(dashOctets).put(toHex(digest).getBytes()).build();
}
else
diff --git a/runtime/binding-grpc/pom.xml b/runtime/binding-grpc/pom.xml
index 2416642240..4cb77e58c5 100644
--- a/runtime/binding-grpc/pom.xml
+++ b/runtime/binding-grpc/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-grpc/src/main/java/io/aklivity/zilla/runtime/binding/grpc/internal/stream/GrpcServerFactory.java b/runtime/binding-grpc/src/main/java/io/aklivity/zilla/runtime/binding/grpc/internal/stream/GrpcServerFactory.java
index c3ead3e2ee..d91738fba8 100644
--- a/runtime/binding-grpc/src/main/java/io/aklivity/zilla/runtime/binding/grpc/internal/stream/GrpcServerFactory.java
+++ b/runtime/binding-grpc/src/main/java/io/aklivity/zilla/runtime/binding/grpc/internal/stream/GrpcServerFactory.java
@@ -299,6 +299,7 @@ public MessageConsumer newStream(
final OctetsFW extension = begin.extension();
final HttpBeginExFW httpBeginEx = extension.get(httpBeginExRO::tryWrap);
+ @SuppressWarnings("resource")
MessageConsumer newStream = (t, b, i, l) -> {};
if (!isGrpcRequestMethod(httpBeginEx))
diff --git a/runtime/binding-grpc/src/main/zilla/protocol.idl b/runtime/binding-grpc/src/main/zilla/protocol.idl
index 30ef20e8fb..9643ceffda 100644
--- a/runtime/binding-grpc/src/main/zilla/protocol.idl
+++ b/runtime/binding-grpc/src/main/zilla/protocol.idl
@@ -18,10 +18,10 @@ scope protocol
scope codec
{
- struct GrpcMessage
- {
- uint8 flag;
- int32 length;
- }
+ struct GrpcMessage
+ {
+ uint8 flag;
+ int32 length;
+ }
}
}
diff --git a/runtime/binding-http-filesystem/pom.xml b/runtime/binding-http-filesystem/pom.xml
index ad3c8d0c38..f4c89c534e 100644
--- a/runtime/binding-http-filesystem/pom.xml
+++ b/runtime/binding-http-filesystem/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-http-kafka/pom.xml b/runtime/binding-http-kafka/pom.xml
index 0f42c6e539..4555a8ce16 100644
--- a/runtime/binding-http-kafka/pom.xml
+++ b/runtime/binding-http-kafka/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-http/pom.xml b/runtime/binding-http/pom.xml
index 9c9f217797..6eb2903986 100644
--- a/runtime/binding-http/pom.xml
+++ b/runtime/binding-http/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfig.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfig.java
index 2edb587c60..004a0ab12d 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfig.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfig.java
@@ -15,12 +15,17 @@
*/
package io.aklivity.zilla.runtime.binding.http.config;
+import java.util.Map;
import java.util.function.Function;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
import io.aklivity.zilla.runtime.engine.config.WithConfig;
public final class HttpWithConfig extends WithConfig
{
+ public final Map overrides;
+
public static HttpWithConfigBuilder builder()
{
return new HttpWithConfigBuilder<>(HttpWithConfig.class::cast);
@@ -33,8 +38,10 @@ public static HttpWithConfigBuilder builder(
}
HttpWithConfig(
- long compositeId)
+ long compositeId,
+ Map overrides)
{
super(compositeId);
+ this.overrides = overrides;
}
}
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfigBuilder.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfigBuilder.java
index bd047a7cf4..0581f45244 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfigBuilder.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/config/HttpWithConfigBuilder.java
@@ -17,8 +17,12 @@
import static io.aklivity.zilla.runtime.engine.config.WithConfig.NO_COMPOSITE_ID;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.function.Function;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
import io.aklivity.zilla.runtime.engine.config.ConfigBuilder;
import io.aklivity.zilla.runtime.engine.config.WithConfig;
@@ -27,6 +31,7 @@ public final class HttpWithConfigBuilder extends ConfigBuilder mapper;
private long compositeId = NO_COMPOSITE_ID;
+ private Map overrides;
HttpWithConfigBuilder(
Function mapper)
@@ -34,6 +39,17 @@ public final class HttpWithConfigBuilder extends ConfigBuilder builder()
+ {
+ return new HttpWithConfigBuilder<>(HttpWithConfig.class::cast);
+ }
+
+ public static HttpWithConfigBuilder builder(
+ Function mapper)
+ {
+ return new HttpWithConfigBuilder<>(mapper);
+ }
+
@Override
@SuppressWarnings("unchecked")
protected Class> thisType()
@@ -48,8 +64,20 @@ public HttpWithConfigBuilder compositeId(
return this;
}
+ public HttpWithConfigBuilder override(
+ String8FW name,
+ String16FW value)
+ {
+ if (overrides == null)
+ {
+ overrides = new LinkedHashMap<>();
+ }
+ overrides.put(name, value);
+ return this;
+ }
+
public T build()
{
- return mapper.apply(new HttpWithConfig(compositeId));
+ return mapper.apply(new HttpWithConfig(compositeId, overrides));
}
}
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpBindingConfig.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpBindingConfig.java
index 05a6eea2c4..7dbb8ad5a5 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpBindingConfig.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpBindingConfig.java
@@ -78,7 +78,8 @@ public HttpBindingConfig(
this.name = binding.name;
this.kind = binding.kind;
this.options = HttpOptionsConfig.class.cast(binding.options);
- this.routes = binding.routes.stream().map(HttpRouteConfig::new).collect(toList());
+ this.routes = binding.routes.stream().map(route ->
+ new HttpRouteConfig(route, options != null ? options.overrides : null)).collect(toList());
this.resolveId = binding.resolveId;
this.credentials = options != null && options.authorization != null ?
asAccessor(options.authorization.credentials) : DEFAULT_CREDENTIALS;
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpConditionMatcher.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpConditionMatcher.java
index 253040c8b3..ad5c4bacdc 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpConditionMatcher.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpConditionMatcher.java
@@ -17,6 +17,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -26,6 +27,7 @@
public final class HttpConditionMatcher
{
private final Map headersMatch;
+ private Consumer observer;
public HttpConditionMatcher(
HttpConditionConfig condition)
@@ -33,6 +35,28 @@ public HttpConditionMatcher(
this.headersMatch = condition.headers != null ? asMatcherMap(condition.headers) : null;
}
+ public void observe(
+ Consumer observer)
+ {
+ this.observer = observer;
+ }
+
+ private boolean observeMatched()
+ {
+ if (observer != null)
+ {
+ observer.accept(this);
+ }
+
+ return true;
+ }
+
+ public String parameter(
+ String name)
+ {
+ return headersMatch.get(":path").group(name);
+ }
+
public boolean matches(
Function headerByName)
{
@@ -49,7 +73,7 @@ public boolean matches(
}
}
- return match;
+ return match && observeMatched();
}
private static Map asMatcherMap(
@@ -64,7 +88,10 @@ private static Matcher asMatcher(
String header,
String wildcard)
{
- String pattern = wildcard.replace(".", "\\.").replace("*", ".*");
+ String pattern = wildcard
+ .replace(".", "\\.")
+ .replace("*", ".*")
+ .replaceAll("\\{([a-zA-Z_]+)\\}", "(?<$1>.+)");
if (":path".equals(header) && !pattern.endsWith(".*"))
{
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpRouteConfig.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpRouteConfig.java
index 740f9b1702..f0e2a16e34 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpRouteConfig.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpRouteConfig.java
@@ -18,12 +18,17 @@
import static io.aklivity.zilla.runtime.engine.config.WithConfig.NO_COMPOSITE_ID;
import static java.util.stream.Collectors.toList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.function.Function;
import java.util.function.LongPredicate;
import io.aklivity.zilla.runtime.binding.http.config.HttpConditionConfig;
import io.aklivity.zilla.runtime.binding.http.config.HttpWithConfig;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
import io.aklivity.zilla.runtime.engine.config.RouteConfig;
public final class HttpRouteConfig
@@ -31,24 +36,44 @@ public final class HttpRouteConfig
public final long id;
private final List when;
- private final HttpWithConfig with;
+ private final HttpWithResolver with;
private final LongPredicate authorized;
+ private final Map overrides;
public HttpRouteConfig(
- RouteConfig route)
+ RouteConfig route,
+ Map overrides)
{
this.id = route.id;
+ this.with = Optional.ofNullable(route.with)
+ .map(HttpWithConfig.class::cast)
+ .map(HttpWithResolver::new)
+ .orElse(null);
this.when = route.when.stream()
.map(HttpConditionConfig.class::cast)
.map(HttpConditionMatcher::new)
+ .peek(m -> Optional.ofNullable(with).ifPresent(w -> m.observe(w::onConditionMatched)))
.collect(toList());
- this.with = (HttpWithConfig)route.with;
this.authorized = route.authorized;
+ this.overrides = new LinkedHashMap<>();
+ if (overrides != null)
+ {
+ this.overrides.putAll(overrides);
+ }
}
public long compositeId()
{
- return with != null ? with.compositeId : NO_COMPOSITE_ID;
+ return with != null ? with.compositeId() : NO_COMPOSITE_ID;
+ }
+
+ public Map overrides()
+ {
+ if (with != null)
+ {
+ overrides.putAll(with.resolveOverrides());
+ }
+ return overrides;
}
boolean authorized(
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapter.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapter.java
new file mode 100644
index 0000000000..43ec50f2f8
--- /dev/null
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.binding.http.internal.config;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonString;
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+import io.aklivity.zilla.runtime.binding.http.config.HttpWithConfig;
+import io.aklivity.zilla.runtime.binding.http.config.HttpWithConfigBuilder;
+import io.aklivity.zilla.runtime.binding.http.internal.HttpBinding;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
+import io.aklivity.zilla.runtime.engine.config.WithConfig;
+import io.aklivity.zilla.runtime.engine.config.WithConfigAdapterSpi;
+
+public class HttpWithConfigAdapter implements WithConfigAdapterSpi, JsonbAdapter
+{
+ private static final String HEADERS_NAME = "headers";
+ private static final String OVERRIDES_NAME = "overrides";
+
+ @Override
+ public String type()
+ {
+ return HttpBinding.NAME;
+ }
+
+ @Override
+ public JsonObject adaptToJson(
+ WithConfig with)
+ {
+ HttpWithConfig config = (HttpWithConfig) with;
+
+ JsonObjectBuilder object = Json.createObjectBuilder();
+
+ if (config.overrides != null &&
+ !config.overrides.isEmpty())
+ {
+ JsonObjectBuilder entries = Json.createObjectBuilder();
+ config.overrides.forEach((k, v) -> entries.add(k.asString(), v.asString()));
+
+ object.add(HEADERS_NAME, object.add(OVERRIDES_NAME, entries));
+ }
+
+ return object.build();
+ }
+
+ @Override
+ public WithConfig adaptFromJson(
+ JsonObject object)
+ {
+ HttpWithConfigBuilder with = HttpWithConfigBuilder.builder();
+
+ if (object.containsKey(HEADERS_NAME))
+ {
+ JsonObject headers = object.getJsonObject(HEADERS_NAME);
+ if (headers.containsKey(OVERRIDES_NAME))
+ {
+ headers.getJsonObject(OVERRIDES_NAME)
+ .forEach((k, v) ->
+ with.override(new String8FW(k), new String16FW(JsonString.class.cast(v).getString())));
+ }
+ }
+
+ return with.build();
+ }
+}
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithResolver.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithResolver.java
new file mode 100644
index 0000000000..df3967a712
--- /dev/null
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithResolver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.binding.http.internal.config;
+
+import static io.aklivity.zilla.runtime.engine.config.WithConfig.NO_COMPOSITE_ID;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import io.aklivity.zilla.runtime.binding.http.config.HttpWithConfig;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
+
+public class HttpWithResolver
+{
+ private static final Pattern PARAMS_PATTERN = Pattern.compile("\\$\\{params\\.([a-zA-Z_]+)\\}");
+
+ private final Matcher paramsMatcher;
+ private final HttpWithConfig with;
+
+ private Function replacer = r -> null;
+
+ public HttpWithResolver(
+ HttpWithConfig with)
+ {
+ this.paramsMatcher = PARAMS_PATTERN.matcher("");
+ this.with = with;
+ }
+
+ public void onConditionMatched(
+ HttpConditionMatcher condition)
+ {
+ this.replacer = r -> condition.parameter(r.group(1));
+ }
+
+ public long compositeId()
+ {
+ return with != null ? with.compositeId : NO_COMPOSITE_ID;
+ }
+
+ public Map resolveOverrides()
+ {
+ Map overrides = new LinkedHashMap<>();
+ if (with != null && with.overrides != null)
+ {
+ with.overrides.forEach((k, v) ->
+ {
+ String value = v.asString();
+ Matcher overrideMatcher = paramsMatcher.reset(value);
+ if (overrideMatcher.find())
+ {
+ value = overrideMatcher.replaceAll(replacer);
+ }
+ overrides.put(k, new String16FW(value));
+ });
+ }
+ return overrides;
+ }
+}
diff --git a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/stream/HttpServerFactory.java b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/stream/HttpServerFactory.java
index ac13062a0b..9b07035fe3 100644
--- a/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/stream/HttpServerFactory.java
+++ b/runtime/binding-http/src/main/java/io/aklivity/zilla/runtime/binding/http/internal/stream/HttpServerFactory.java
@@ -1072,9 +1072,10 @@ else if (!isCorsRequestAllowed(server.binding, headers))
HttpRouteConfig route = binding.resolve(exchangeAuth, headers::get);
if (route != null)
{
- if (binding.options != null && binding.options.overrides != null)
+ Map overrides = route.overrides();
+ if (overrides != null)
{
- binding.options.overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
+ overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
final HttpBeginExFW.Builder newBeginEx = newBeginExRW.wrap(codecBuffer, 0, codecBuffer.capacity())
.typeId(httpTypeId);
@@ -1274,13 +1275,13 @@ else if (!"chunked".equals(value))
break;
case "upgrade":
- if (server.decoder != decodeHeadersOnly)
+ if ("h2c".equals(value))
{
- error = ERROR_400_BAD_REQUEST;
+ // TODO: h2c
}
- else if ("h2c".equals(value))
+ else if (server.decoder != decodeHeadersOnly)
{
- // TODO: h2c
+ error = ERROR_400_BAD_REQUEST;
}
else
{
@@ -5014,9 +5015,10 @@ else if (!isCorsRequestAllowed(binding, headers))
HttpPolicyConfig policy = binding.access().effectivePolicy(headers);
final String origin = policy == CROSS_ORIGIN ? headers.get(HEADER_NAME_ORIGIN) : null;
- if (binding.options != null && binding.options.overrides != null)
+ Map overrides = route.overrides();
+ if (overrides != null)
{
- binding.options.overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
+ overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
}
final HttpBeginExFW beginEx = beginExRW.wrap(extBuffer, 0, extBuffer.capacity())
@@ -5395,9 +5397,10 @@ private void doEncodePromise(
if (pushId != -1)
{
- if (binding.options != null && binding.options.overrides != null)
+ Map overrides = route.overrides();
+ if (overrides != null)
{
- binding.options.overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
+ overrides.forEach((k, v) -> headers.put(k.asString(), v.asString()));
}
final long originId = this.routedId;
diff --git a/runtime/binding-http/src/main/moditect/module-info.java b/runtime/binding-http/src/main/moditect/module-info.java
index b837b223eb..337a22e0e8 100644
--- a/runtime/binding-http/src/main/moditect/module-info.java
+++ b/runtime/binding-http/src/main/moditect/module-info.java
@@ -30,4 +30,7 @@
provides io.aklivity.zilla.runtime.engine.event.EventFormatterFactorySpi
with io.aklivity.zilla.runtime.binding.http.internal.HttpEventFormatterFactory;
+
+ provides io.aklivity.zilla.runtime.engine.config.WithConfigAdapterSpi
+ with io.aklivity.zilla.runtime.binding.http.internal.config.HttpWithConfigAdapter;
}
diff --git a/runtime/binding-http/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.config.WithConfigAdapterSpi b/runtime/binding-http/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.config.WithConfigAdapterSpi
new file mode 100644
index 0000000000..60e65efc04
--- /dev/null
+++ b/runtime/binding-http/src/main/resources/META-INF/services/io.aklivity.zilla.runtime.engine.config.WithConfigAdapterSpi
@@ -0,0 +1 @@
+io.aklivity.zilla.runtime.binding.http.internal.config.HttpWithConfigAdapter
diff --git a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapterTest.java b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapterTest.java
new file mode 100644
index 0000000000..77072c7086
--- /dev/null
+++ b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/config/HttpWithConfigAdapterTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.binding.http.internal.config;
+
+import static java.util.Collections.singletonMap;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import io.aklivity.zilla.runtime.binding.http.config.HttpWithConfig;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String16FW;
+import io.aklivity.zilla.runtime.binding.http.internal.types.String8FW;
+
+public class HttpWithConfigAdapterTest
+{
+ private Jsonb jsonb;
+
+ @Before
+ public void initJson()
+ {
+ JsonbConfig config = new JsonbConfig()
+ .withAdapters(new HttpWithConfigAdapter());
+ jsonb = JsonbBuilder.create(config);
+ }
+
+ @Test
+ public void shouldReadWith()
+ {
+ String text =
+ "{" +
+ "\"headers\":" +
+ "{" +
+ "\"overrides\":" +
+ "{" +
+ "\":authority\":\"example.com:443\"" +
+ "}" +
+ "}" +
+ "}";
+
+ HttpWithConfig with = jsonb.fromJson(text, HttpWithConfig.class);
+
+ assertThat(with, not(nullValue()));
+ assertThat(with.overrides, equalTo(singletonMap(new String8FW(":authority"), new String16FW("example.com:443"))));
+ }
+
+ @Test
+ public void shouldWriteWith()
+ {
+ HttpWithConfig with = HttpWithConfig.builder()
+ .override(new String8FW(":authority"), new String16FW("example.com:443")).build();
+
+ String text = jsonb.toJson(with);
+
+ String expected =
+ "{" +
+ "\"headers\":" +
+ "{" +
+ "\"overrides\":" +
+ "{" +
+ "\":authority\":\"example.com:443\"" +
+ "}" +
+ "}" +
+ "}";
+
+ assertThat(text, not(nullValue()));
+ assertThat(text, equalTo(expected));
+ }
+}
diff --git a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7230/server/ConnectionManagementIT.java b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7230/server/ConnectionManagementIT.java
index b4d836ddc3..6929a57c79 100644
--- a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7230/server/ConnectionManagementIT.java
+++ b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7230/server/ConnectionManagementIT.java
@@ -69,6 +69,16 @@ public void shouldSendRequestWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Test
+ @Configuration("server.with.route.header.overrides.yaml")
+ @Specification({
+ "${net}/request.with.route.header.overrides/client",
+ "${app}/request.with.route.header.overrides/server" })
+ public void shouldSendRequestWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Configuration("server.yaml")
@Specification({
diff --git a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/ConnectionManagementIT.java b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/ConnectionManagementIT.java
index 1df4192ad0..430788c466 100644
--- a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/ConnectionManagementIT.java
+++ b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/ConnectionManagementIT.java
@@ -82,6 +82,16 @@ public void httpGetExchangeWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Test
+ @Configuration("server.with.route.header.overrides.yaml")
+ @Specification({
+ "${net}/http.get.exchange.with.route.header.overrides/client",
+ "${app}/http.get.exchange.with.route.header.overrides/server" })
+ public void httpGetExchangeWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Configuration("server.yaml")
@Specification({
diff --git a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/StartingIT.java b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/StartingIT.java
index c0c9f9fc89..353a3d4bcc 100644
--- a/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/StartingIT.java
+++ b/runtime/binding-http/src/test/java/io/aklivity/zilla/runtime/binding/http/internal/streams/rfc7540/server/StartingIT.java
@@ -79,6 +79,16 @@ public void shouldNotUpgradeViaCleartextWithTlsAndNoAlpn() throws Exception
k3po.finish();
}
+ @Test
+ @Configuration("server.yaml")
+ @Specification({
+ "${net}/upgrade.h2c.with.no.settings/client",
+ "${app}/upgrade.http/server" })
+ public void shouldUpgradeViaCleartextWithNoSettings() throws Exception
+ {
+ k3po.finish();
+ }
+
@Ignore("TODO")
@Test
@Configuration("server.yaml")
diff --git a/runtime/binding-kafka-grpc/pom.xml b/runtime/binding-kafka-grpc/pom.xml
index 4b5989d488..8737cc65df 100644
--- a/runtime/binding-kafka-grpc/pom.xml
+++ b/runtime/binding-kafka-grpc/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-kafka/pom.xml b/runtime/binding-kafka/pom.xml
index 2a346dcd47..551d2c6310 100644
--- a/runtime/binding-kafka/pom.xml
+++ b/runtime/binding-kafka/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/cache/KafkaCachePartition.java b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/cache/KafkaCachePartition.java
index 5e5ff2402b..e043c21e25 100644
--- a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/cache/KafkaCachePartition.java
+++ b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/cache/KafkaCachePartition.java
@@ -128,7 +128,7 @@ public final class KafkaCachePartition
private final KafkaCachePaddedKeyFW.Builder paddedKeyRW = new KafkaCachePaddedKeyFW.Builder()
.wrap(new UnsafeBuffer(new byte[8192]), 0, 8192);;
private final String32FW.Builder stringRW = new String32FW.Builder()
- .wrap(new UnsafeBuffer(new byte[256]), 0, 256);;
+ .wrap(new UnsafeBuffer(new byte[256]), 0, 256);
private final Varint32FW.Builder varintRW = new Varint32FW.Builder().wrap(new UnsafeBuffer(new byte[5]), 0, 5);
private final Array32FW headersRO = new Array32FW(new KafkaHeaderFW());
private final Array32FW.Builder trailersRW =
diff --git a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaCacheClientProduceFactory.java b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaCacheClientProduceFactory.java
index d7d13978ae..c0b2ec05cf 100644
--- a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaCacheClientProduceFactory.java
+++ b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaCacheClientProduceFactory.java
@@ -800,15 +800,15 @@ stream.valueMark, stream.valueLimit, now().toEpochMilli(), stream.initialId, PRO
Array32FW trailers = EMPTY_TRAILERS;
partition.writeProduceEntryFin(stream.segment, stream.entryMark, stream.valueLimit, stream.initialSeq, trailers);
+ markEntryDirty(traceId, stream.partitionOffset);
flushClientFanInitialIfNecessary(traceId);
}
else
{
error = ERROR_RECORD_LIST_TOO_LARGE;
+ markEntryDirty(traceId, stream.partitionOffset);
}
- markEntryDirty(traceId, stream.partitionOffset);
-
if (error != NO_ERROR)
{
stream.cleanupClient(traceId, error);
@@ -1390,6 +1390,8 @@ private void onClientInitialFlush(
}
}
+ initialAck += reserved;
+
final int noAck = (int) (initialSeq - initialAck);
doClientInitialWindow(traceId, noAck, initialBudgetMax);
}
diff --git a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientConnectionPool.java b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientConnectionPool.java
index 12ff00897a..0aed054b56 100644
--- a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientConnectionPool.java
+++ b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientConnectionPool.java
@@ -1651,6 +1651,7 @@ private void cleanupStreams(
streamsByInitialId.remove(s);
});
streams.clear();
+ signalerCorrelations.clear();
}
private void onConnectionSignal(
@@ -1808,11 +1809,7 @@ private void cleanupConnection(
doConnectionAbort(traceId);
doConnectionReset(traceId);
- streams.clear();
- requests.clear();
- responses.clear();
- responseAcks.clear();
- signalerCorrelations.clear();
+ cleanupStreams(traceId);
}
private void cleanupBudgetCreditorIfNecessary()
diff --git a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientProduceFactory.java b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientProduceFactory.java
index 7ecd21c10c..c0c713d083 100644
--- a/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientProduceFactory.java
+++ b/runtime/binding-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/KafkaClientProduceFactory.java
@@ -111,7 +111,7 @@ public final class KafkaClientProduceFactory extends KafkaClientSaslHandshaker i
private static final DirectBuffer EMPTY_BUFFER = new UnsafeBuffer();
private static final OctetsFW EMPTY_OCTETS = new OctetsFW().wrap(EMPTY_BUFFER, 0, 0);
private static final Consumer EMPTY_EXTENSION = ex -> {};
- private static final Array32FW EMPTY_HEADER =
+ private static final Array32FW EMPTY_HEADERS =
new Array32FW.Builder<>(new KafkaHeaderFW.Builder(), new KafkaHeaderFW())
.wrap(new UnsafeBuffer(new byte[64]), 0, 64).build();
@@ -128,7 +128,6 @@ public final class KafkaClientProduceFactory extends KafkaClientSaslHandshaker i
private final ExtensionFW extensionRO = new ExtensionFW();
private final KafkaBeginExFW kafkaBeginExRO = new KafkaBeginExFW();
private final KafkaDataExFW kafkaDataExRO = new KafkaDataExFW();
- private final Array32FW kafkaHeaderRO = new Array32FW<>(new KafkaHeaderFW());
private final BeginFW.Builder beginRW = new BeginFW.Builder();
private final DataFW.Builder dataRW = new DataFW.Builder();
@@ -594,7 +593,17 @@ private int flushRecordContFin(
if ((flags & FLAGS_FIN) == FLAGS_FIN)
{
- client.doEncodeRecordFin(traceId, budgetId);
+ Array32FW headers = EMPTY_HEADERS;
+
+ final KafkaDataExFW kafkaDataEx = extension.get(kafkaDataExRO::tryWrap);
+ if (kafkaDataEx != null)
+ {
+ assert kafkaDataEx.kind() == KafkaDataExFW.KIND_PRODUCE;
+ final KafkaProduceDataExFW kafkaProduceDataEx = kafkaDataEx.produce();
+ headers = kafkaProduceDataEx.headers();
+ }
+
+ client.doEncodeRecordFin(traceId, budgetId, headers);
assert progress == limit;
client.flusher = flushRecord;
client.flushFlags = FLAGS_FIN;
@@ -1223,7 +1232,6 @@ private final class KafkaProduceClient extends KafkaSaslClient
private int encodeSlotLimit;
private long encodeSlotTraceId;
- private int flushableRecordHeadersBytes;
private int encodeableRecordBytesDeferred;
private int encodeableRecordBatchSlotOffset;
private int flushableRequestBytes;
@@ -1320,6 +1328,8 @@ private void onNetworkBegin(
}
private long networkBytesReceived;
+ private long headersChecksum;
+ private int headersSize;
private void onNetworkData(
DataFW data)
@@ -1605,8 +1615,7 @@ private void flush(
final int length = limit - progress;
if (encodeSlot != NO_SLOT &&
flushableRequestBytes > 0 &&
- encodeSlotLimit + length + produceRecordFramingSize + flushableRecordHeadersBytes >
- encodePool.slotCapacity())
+ encodeSlotLimit + length + produceRecordFramingSize > encodePool.slotCapacity())
{
doNetworkData(traceId, budgetId, EMPTY_BUFFER, 0, 0);
}
@@ -1738,14 +1747,7 @@ private void doEncodeRecordInit(
encodeSlotBuffer.putInt(encodeableRecordBatchSlotOffset + RecordBatchFW.FIELD_OFFSET_RECORD_COUNT,
recordCount + 1, BIG_ENDIAN);
- if (headersCount > 0)
- {
- flushableRecordHeadersBytes = headers.sizeof();
-
- final int encodeSlotMaxLimit = encodePool.slotCapacity() - flushableRecordHeadersBytes;
- encodeSlotBuffer.putBytes(encodeSlotMaxLimit, headers.buffer(), headers.offset(),
- flushableRecordHeadersBytes);
- }
+ doEncodeHeadersChecksum(headers);
}
private void doEncodeRecordCont(
@@ -1758,8 +1760,7 @@ private void doEncodeRecordCont(
{
final int length = value.sizeof();
- final int encodeableBytes = produceRecordFramingSize + encodeSlotLimit +
- length + flushableRecordHeadersBytes;
+ final int encodeableBytes = produceRecordFramingSize + encodeSlotLimit + length;
if (encodeableBytes >= encodePool.slotCapacity())
{
doEncodeRequestIfNecessary(traceId, budgetId);
@@ -1780,7 +1781,8 @@ private void doEncodeRecordCont(
private void doEncodeRecordFin(
long traceId,
- long budgetId)
+ long budgetId,
+ Array32FW headers)
{
assert encodeSlot != NO_SLOT;
final MutableDirectBuffer encodeSlotBuffer = encodePool.buffer(encodeSlot);
@@ -1789,14 +1791,6 @@ private void doEncodeRecordFin(
final int encodeLimit = writeBuffer.capacity();
int encodeProgress = 0;
- Array32FW headers = EMPTY_HEADER;
- if (flushableRecordHeadersBytes > 0)
- {
- final int encodeSlotMaxLimit = encodePool.slotCapacity();
- headers = kafkaHeaderRO.wrap(encodeSlotBuffer, encodeSlotMaxLimit - flushableRecordHeadersBytes,
- encodeSlotMaxLimit);
- flushableRecordHeadersBytes = 0;
- }
final int headersCount = headers.fieldCount();
final RecordTrailerFW recordTrailer = recordTrailerRW.wrap(encodeBuffer, encodeProgress, encodeLimit)
.headerCount(headersCount)
@@ -1816,7 +1810,7 @@ private void doEncodeRecordFin(
encodeSlotBuffer.putBytes(encodeSlotLimit, encodeBuffer, 0, encodeProgress);
encodeSlotLimit += encodeProgress;
- if (encodeableRecordBytesDeferred > 0)
+ if (encodeableRecordBytesDeferred > 0 && flushableRequestBytes > 0)
{
doNetworkData(traceId, budgetId, EMPTY_BUFFER, 0, 0);
}
@@ -1952,9 +1946,40 @@ private void doEncodeProduceRequest(
decoder = decodeProduceResponse;
}
- private void encodeCrc()
+ private void doEncodeHeadersChecksum(
+ Array32FW headers)
{
final MutableDirectBuffer encodeBuffer = writeBuffer;
+ final int encodeLimit = writeBuffer.capacity();
+
+ int encodeProgress = 0;
+
+ final int headersCount = headers.fieldCount();
+ final RecordTrailerFW recordTrailer = recordTrailerRW.wrap(encodeBuffer, encodeProgress, encodeLimit)
+ .headerCount(headersCount)
+ .build();
+
+ encodeProgress = recordTrailer.limit();
+
+ if (headersCount > 0)
+ {
+ final DirectBuffer headerItems = headers.items();
+ final int headerItemsSize = headerItems.capacity();
+
+ encodeBuffer.putBytes(encodeProgress, headerItems, 0, headerItemsSize);
+ encodeProgress += headerItemsSize;
+ }
+
+ final byte[] encodeByteBuf = encodeBuffer.byteArray();
+ crc32c.reset();
+ crc32c.update(encodeByteBuf, 0, encodeProgress);
+
+ headersChecksum = crc32c.getValue();
+ headersSize = encodeProgress;
+ }
+
+ private void encodeCrc()
+ {
final MutableDirectBuffer encodeSlotBuffer = encodePool.buffer(encodeSlot);
final int encodeLimit = encodeSlotLimit;
@@ -1969,7 +1994,8 @@ private void encodeCrc()
final int crcOffset = recordBatch.offset() + RecordBatchFW.FIELD_OFFSET_CRC;
final int crcDataOffset = recordBatch.offset() + RecordBatchFW.FIELD_OFFSET_ATTRIBUTES;
- final int crcDataLimit = recordBatchLimit <= encodeLimit ? recordBatchLimit : recordBatch.limit();
+ final int recordHeaderLimit = encodeLimit - (valueCompleteSize - encodeableRecordBytesDeferred);
+ final int crcDataLimit = recordBatchLimit <= encodeLimit ? recordBatchLimit : recordHeaderLimit;
final ByteBuffer encodeSlotByteBuffer = encodePool.byteBuffer(encodeSlot);
final int encodePosition = encodeSlotByteBuffer.position();
@@ -1981,10 +2007,10 @@ private void encodeCrc()
crc.update(encodeSlotByteBuffer);
long checksum = crc.getValue();
-
- if (crcDataLimit == recordBatch.limit())
+ if (crcDataLimit == recordHeaderLimit && valueCompleteSize != 0)
{
- checksum = computeChecksum(encodeBuffer, encodeLimit, encodeProgress, encodeSlotBuffer, checksum);
+ checksum = combineCRC32C(checksum, valueChecksum, valueCompleteSize);
+ checksum = combineCRC32C(checksum, headersChecksum, headersSize);
}
encodeSlotBuffer.putInt(crcOffset, (int) checksum, BIG_ENDIAN);
@@ -1993,48 +2019,6 @@ private void encodeCrc()
}
}
- private long computeChecksum(
- MutableDirectBuffer encodeBuffer,
- int encodeLimit,
- int encodeProgress,
- MutableDirectBuffer encodeSlotBuffer,
- long checksum)
- {
- final int oldEncodeProgress = encodeProgress;
-
- checksum = combineCRC32C(checksum, valueChecksum, valueCompleteSize);
-
- Array32FW headers = EMPTY_HEADER;
- if (flushableRecordHeadersBytes > 0)
- {
- final int encodeSlotMaxLimit = encodePool.slotCapacity();
- headers = kafkaHeaderRO.wrap(encodeSlotBuffer, encodeSlotMaxLimit - flushableRecordHeadersBytes,
- encodeSlotMaxLimit);
- }
- final int headersCount = headers.fieldCount();
- final RecordTrailerFW recordTrailer = recordTrailerRW.wrap(encodeBuffer, encodeProgress, encodeLimit)
- .headerCount(headersCount)
- .build();
-
- encodeProgress = recordTrailer.limit();
-
- if (headersCount > 0)
- {
- final DirectBuffer headerItems = headers.items();
- final int headerItemsSize = headerItems.capacity();
-
- encodeBuffer.putBytes(encodeProgress, headerItems, 0, headerItemsSize);
- encodeProgress += headerItemsSize;
- }
-
- final int length = encodeProgress - oldEncodeProgress;
- final byte[] encodeByteBuf = encodeBuffer.byteArray();
- crc32c.reset();
- crc32c.update(encodeByteBuf, oldEncodeProgress, length);
- checksum = combineCRC32C(checksum, crc32c.getValue(), length);
- return checksum;
- }
-
private void encodeNetwork(
long traceId,
long authorization,
@@ -2121,9 +2105,9 @@ private void encodeNetwork(
}
else if (encodeableRecordBytesDeferred > 0 && flushableRequestBytes != 0)
{
- final int encodeBytesBuffered = encodeSlotLimit - encodeSlotOffset + flushableRecordHeadersBytes;
- final int encodeRequestBytesBuffered = Math.max(flushableRequestBytes - encodeableRecordBytesDeferred
- - Math.max(flushableRecordHeadersBytes, 1), 0);
+ final int encodeBytesBuffered = encodeSlotLimit - encodeSlotOffset;
+ final int encodeRequestBytesBuffered =
+ Math.max(flushableRequestBytes - encodeableRecordBytesDeferred - 1, 0);
final int encodeNoAck = Math.max(encodeRequestBytesBuffered, encodeBytesBuffered);
final int noAck = (int) (stream.initialSeq - stream.initialAck);
stream.doAppWindow(traceId, noAck, noAck + encodeMaxBytes - encodeNoAck);
@@ -2310,9 +2294,9 @@ private void onDecodeProduceResponse(
{
nextResponseId++;
- final int encodeBytesBuffered = encodeSlotLimit - encodeSlotOffset + flushableRecordHeadersBytes;
- final int encodeRequestBytesBuffered = Math.max(flushableRequestBytes - encodeableRecordBytesDeferred
- - Math.max(flushableRecordHeadersBytes, 1), 0);
+ final int encodeBytesBuffered = encodeSlotLimit - encodeSlotOffset;
+ final int encodeRequestBytesBuffered =
+ Math.max(flushableRequestBytes - encodeableRecordBytesDeferred - 1, 0);
final int encodeNoAck = Math.max(encodeRequestBytesBuffered, encodeBytesBuffered);
stream.doAppWindow(traceId, encodeNoAck, encodeMaxBytes);
diff --git a/runtime/binding-kafka/src/test/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/ClientProduceIT.java b/runtime/binding-kafka/src/test/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/ClientProduceIT.java
index 3d71429543..79b37e51fd 100644
--- a/runtime/binding-kafka/src/test/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/ClientProduceIT.java
+++ b/runtime/binding-kafka/src/test/java/io/aklivity/zilla/runtime/binding/kafka/internal/stream/ClientProduceIT.java
@@ -203,6 +203,36 @@ public void shouldSendMessageValue() throws Exception
k3po.finish();
}
+ @Test
+ @Configuration("client.when.topic.yaml")
+ @Specification({
+ "${app}/message.empty.crc/client",
+ "${net}/message.empty.crc/server"})
+ public void shouldSendMessageEmptyWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.when.topic.yaml")
+ @Specification({
+ "${app}/message.null.crc/client",
+ "${net}/message.null.crc/server"})
+ public void shouldSendMessageNullWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Configuration("client.when.topic.yaml")
+ @Specification({
+ "${app}/messages.fragmented.crc/client",
+ "${net}/messages.fragmented.crc/server"})
+ public void shouldSendFragmentedMessagesWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Configuration("client.when.topic.yaml")
@Specification({
@@ -327,6 +357,17 @@ public void shouldSendMessageValueRepeated() throws Exception
k3po.finish();
}
+ @Test
+ @Configuration("client.when.topic.yaml")
+ @Specification({
+ "${app}/message.value.repeated.fragmented/client",
+ "${net}/message.value.repeated/server"})
+ @Configure(name = KafkaConfigurationTest.KAFKA_CLIENT_PRODUCE_MAX_REQUEST_MILLIS_NAME, value = "200")
+ public void shouldSendMessageValueRepeatedWhenFragmented() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Configuration("client.when.topic.yaml")
@Specification({
diff --git a/runtime/binding-mqtt-kafka/pom.xml b/runtime/binding-mqtt-kafka/pom.xml
index c1947c80f2..bd3382f031 100644
--- a/runtime/binding-mqtt-kafka/pom.xml
+++ b/runtime/binding-mqtt-kafka/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/config/MqttKafkaConditionMatcher.java b/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/config/MqttKafkaConditionMatcher.java
index 7d1af3446c..70c0f01a24 100644
--- a/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/config/MqttKafkaConditionMatcher.java
+++ b/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/config/MqttKafkaConditionMatcher.java
@@ -14,7 +14,6 @@
*/
package io.aklivity.zilla.runtime.binding.mqtt.kafka.internal.config;
-import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
@@ -65,22 +64,6 @@ private boolean observeMatched()
return true;
}
- private static List asTopicMatchers(
- List wildcards)
- {
- final List matchers = new ArrayList<>();
- for (String wildcard : wildcards)
- {
- String patternBegin = wildcard.startsWith("/") ? "(" : "^(?!\\/)(";
- String fixedPattern = patternBegin + asRegexPattern(wildcard, 0, true) + ")?\\/?\\#?";
- String nonFixedPattern = patternBegin + asRegexPattern(wildcard, 0, false) + ")?\\/?\\#";
- fixedPattern = fixedPattern.replaceAll("\\{([a-zA-Z_]+)\\}", "(?<$1>.+)");
- nonFixedPattern = nonFixedPattern.replaceAll("\\{([a-zA-Z_]+)\\}", "");
- matchers.add(Pattern.compile(nonFixedPattern + "|" + fixedPattern).matcher(""));
- }
- return matchers;
- }
-
private static Matcher asTopicMatcher(
List wildcards)
{
diff --git a/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/stream/MqttKafkaPublishFactory.java b/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/stream/MqttKafkaPublishFactory.java
index dee573d4c3..5697efb2e4 100644
--- a/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/stream/MqttKafkaPublishFactory.java
+++ b/runtime/binding-mqtt-kafka/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/kafka/internal/stream/MqttKafkaPublishFactory.java
@@ -610,7 +610,7 @@ private void onMqttData(
.capabilities(c -> c.set(KafkaCapabilities.PRODUCE_ONLY))
.key(key)))
.build();
- retained.doKafkaFlush(traceId, authorization, budgetId, kafkaFlushEx);
+ retained.doKafkaFlush(traceId, authorization, budgetId, reserved, kafkaFlushEx);
}
}
@@ -1629,7 +1629,7 @@ else if (delegate.qos < MqttQoS.EXACTLY_ONCE.value())
.typeId(kafkaTypeId)
.merged(m -> m.produce(p -> p.hashKey(hashKey)))
.build();
- doKafkaFlush(traceId, authorization, 0, kafkaFlushEx);
+ doKafkaFlush(traceId, authorization, 0, 0, kafkaFlushEx);
}
}
@@ -1674,12 +1674,13 @@ private void doKafkaFlush(
long traceId,
long authorization,
long budgetId,
+ int reserved,
KafkaFlushExFW extension)
{
doFlush(kafka, originId, routedId, initialId, initialSeq, initialAck, initialMax,
- traceId, authorization, budgetId, initialPad, extension);
+ traceId, authorization, budgetId, reserved, extension);
- initialSeq += initialPad;
+ initialSeq += reserved;
assert initialSeq <= initialAck + initialMax;
}
diff --git a/runtime/binding-mqtt/pom.xml b/runtime/binding-mqtt/pom.xml
index 7520bea62e..5d79b9fa8c 100644
--- a/runtime/binding-mqtt/pom.xml
+++ b/runtime/binding-mqtt/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttClientFactory.java b/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttClientFactory.java
index d979b67d0b..8a1af94e87 100644
--- a/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttClientFactory.java
+++ b/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttClientFactory.java
@@ -944,7 +944,6 @@ private int decodePublish(
int progress = offset;
- decode:
if (length >= client.decodeablePacketBytes)
{
int reasonCode = SUCCESS;
@@ -1051,7 +1050,7 @@ private int decodePublishPayload(
if (canPublish && subscriber.debitorIndex != NO_DEBITOR_INDEX && reserved != 0)
{
final int minimum = reserved; // TODO: fragmentation
- reserved = subscriber.debitor.claim(subscriber.debitorIndex, subscriber.replyId, minimum, reserved);
+ reserved = subscriber.debitor.claim(traceId, subscriber.debitorIndex, subscriber.replyId, minimum, reserved, 0);
}
if (canPublish && (reserved != 0 || payloadSize == 0))
diff --git a/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttServerFactory.java b/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttServerFactory.java
index 6e012eef4b..3e728870fc 100644
--- a/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttServerFactory.java
+++ b/runtime/binding-mqtt/src/main/java/io/aklivity/zilla/runtime/binding/mqtt/internal/stream/MqttServerFactory.java
@@ -1326,7 +1326,7 @@ private int decodePublishV4(
// special case, when payload is empty -> wait for window
break decode;
}
- server.publishPayloadBytes = decodeablePublishPayloadBytes;
+ server.decodeablePublishPayloadBytes = decodeablePublishPayloadBytes;
server.decodeablePacketBytes = limit - publishLimit;
server.decoder = decodePublishPayload;
progress = publishLimit;
@@ -1462,7 +1462,7 @@ private int decodePublishV5(
// special case, when payload is empty -> wait for window
break decode;
}
- server.publishPayloadBytes = decodeablePublishPayloadBytes;
+ server.decodeablePublishPayloadBytes = decodeablePublishPayloadBytes;
server.decodeablePacketBytes = limit - publishLimit;
server.decoder = decodePublishPayload;
progress = publishLimit;
@@ -1492,30 +1492,32 @@ private int decodePublishPayload(
int progress = offset;
int reasonCode = SUCCESS;
- decode:
if (length >= 0)
{
MqttServer.MqttPublishStream publisher = server.publishes.get(server.decodePublisherKey);
- int publishablePayloadSize =
- Math.min(Math.min(server.publishPayloadBytes, publisher.initialBudget()), length);
-
- final OctetsFW payload = payloadRO.wrap(buffer, offset, offset + publishablePayloadSize);
+ int initialBudget = publisher.initialBudget();
+ int lengthMax = Math.min(length, server.decodeablePublishPayloadBytes);
+ int reservedMax = Math.max(publisher.initialPad, Math.min(lengthMax + publisher.initialPad, initialBudget));
boolean canPublish = MqttState.initialOpened(publisher.state);
- final int maximum = publishablePayloadSize;
- final int minimum = Math.min(maximum, Math.max(publisher.initialMin, 1024));
+ final int maximum = reservedMax;
+ final int minimum = Math.min(maximum, Math.max(publisher.initialMin, 1024) + publisher.initialPad);
int valueClaimed = maximum;
- if (canPublish && publisher.debitorIndex != NO_DEBITOR_INDEX && publishablePayloadSize != 0)
+ if (canPublish && publisher.debitorIndex != NO_DEBITOR_INDEX && lengthMax != 0)
{
valueClaimed =
- publisher.debitor.claim(publisher.debitorIndex, publisher.initialId, minimum, maximum);
+ publisher.debitor.claim(traceId, publisher.debitorIndex, publisher.initialId, minimum, maximum, 0);
}
- if (canPublish && (valueClaimed != 0 || payload.sizeof() == 0))
+ int sizeClaimed = valueClaimed - publisher.initialPad;
+
+ final OctetsFW payload = payloadRO.wrap(buffer, offset, offset + sizeClaimed);
+
+ if (canPublish && (sizeClaimed != 0 || payload.sizeof() == 0))
{
if (server.publishPayloadDeferred == 0)
{
@@ -1526,12 +1528,12 @@ private int decodePublishPayload(
server.onDecodePublishPayload(traceId, authorization, valueClaimed, server.decodedPacketId, server.decodedQos,
server.decodedFlags, server.decodedExpiryInterval, server.decodedContentType, server.decodedPayloadFormat,
server.decodedResponseTopic, server.decodedCorrelationData, server.decodedUserProperties,
- payload, payload.offset(), payload.offset() + valueClaimed, publisher.contentType);
+ payload, payload.offset(), payload.offset() + sizeClaimed, publisher.contentType);
- progress = payload.offset() + valueClaimed;
+ progress = payload.offset() + sizeClaimed;
- if (server.publishPayloadBytes == 0)
+ if (server.decodeablePublishPayloadBytes == 0)
{
server.decoder = decodePacketTypeByVersion.get(server.version);
}
@@ -2448,7 +2450,7 @@ private final class MqttServer
private long decodePublisherKey;
private int decodeablePacketBytes;
private int publishPayloadDeferred;
- public int publishPayloadBytes;
+ public int decodeablePublishPayloadBytes;
private int willPayloadDeferred;
public int willPayloadBytes;
@@ -2949,7 +2951,7 @@ else if (this.authField.equals(MqttConnectProperty.PASSWORD))
this.sessionId = sessionAuth;
- this.session = new MqttSessionStream(originId, resolved.id, 0);
+ this.session = new MqttSessionStream(routedId, resolved.id, 0);
final int capabilities = versions.contains(MqttVersion.V_5) && versions.size() == 1
? REDIRECT_MASK : 0;
@@ -3080,79 +3082,78 @@ private int onDecodeConnectWillPayload(
int limit)
{
int progress = offset;
- decode:
- {
- final int willFlags = decodeWillFlags(connectFlags);
- final int willQos = decodeWillQos(connectFlags);
- final boolean willFlagSet = isSetWillFlag(connectFlags);
- if (willFlagSet && MqttState.initialOpened(session.state))
- {
- int publishedWillSize = 0;
- if (willPayloadDeferred == 0)
+ final int willFlags = decodeWillFlags(connectFlags);
+ final int willQos = decodeWillQos(connectFlags);
+ final boolean willFlagSet = isSetWillFlag(connectFlags);
+
+ if (willFlagSet && MqttState.initialOpened(session.state))
+ {
+ int publishedWillSize = 0;
+ if (willPayloadDeferred == 0)
+ {
+ final MqttWillMessageFW.Builder willMessageBuilder =
+ mqttWillMessageRW.wrap(willMessageBuffer, 0, willMessageBuffer.capacity())
+ .topic(mqttConnectPayloadRO.willTopic)
+ .delay(mqttConnectPayloadRO.willDelay)
+ .qos(willQos)
+ .flags(willFlags)
+ .expiryInterval(mqttConnectPayloadRO.expiryInterval)
+ .contentType(mqttConnectPayloadRO.contentType)
+ .format(f -> f.set(mqttConnectPayloadRO.payloadFormat))
+ .responseTopic(mqttConnectPayloadRO.responseTopic)
+ .correlation(c -> c.bytes(mqttConnectPayloadRO.correlationData))
+ .payloadSize(mqttConnectPayloadRO.payloadSize);
+
+ if (version == 5)
{
- final MqttWillMessageFW.Builder willMessageBuilder =
- mqttWillMessageRW.wrap(willMessageBuffer, 0, willMessageBuffer.capacity())
- .topic(mqttConnectPayloadRO.willTopic)
- .delay(mqttConnectPayloadRO.willDelay)
- .qos(willQos)
- .flags(willFlags)
- .expiryInterval(mqttConnectPayloadRO.expiryInterval)
- .contentType(mqttConnectPayloadRO.contentType)
- .format(f -> f.set(mqttConnectPayloadRO.payloadFormat))
- .responseTopic(mqttConnectPayloadRO.responseTopic)
- .correlation(c -> c.bytes(mqttConnectPayloadRO.correlationData))
- .payloadSize(mqttConnectPayloadRO.payloadSize);
-
- if (version == 5)
- {
- final Array32FW userProperties = willUserPropertiesRW.build();
- userProperties.forEach(
- c -> willMessageBuilder.propertiesItem(p -> p.key(c.key()).value(c.value())));
- }
-
- final MqttWillMessageFW will = willMessageBuilder.build();
- final int headerSize = willMessageBuilder.sizeof();
- int payloadSize = Math.min(limit - offset, session.initialBudget() - headerSize);
+ final Array32FW userProperties = willUserPropertiesRW.build();
+ userProperties.forEach(
+ c -> willMessageBuilder.propertiesItem(p -> p.key(c.key()).value(c.value())));
+ }
- final OctetsFW payload = payloadRO.wrap(buffer, offset, offset + payloadSize);
+ final MqttWillMessageFW will = willMessageBuilder.build();
+ final int headerSize = willMessageBuilder.sizeof();
+ int payloadSize = Math.min(limit - offset, session.initialBudget() - headerSize);
- willMessageBuffer.putBytes(will.limit(), payload.buffer(), payload.offset(), payload.limit());
+ final OctetsFW payload = payloadRO.wrap(buffer, offset, offset + payloadSize);
- int flags = willPayloadBytes + headerSize > session.initialBudget() ? FLAG_INIT : FLAG_INIT | FLAG_FIN;
- int deferred = Math.max(willPayloadBytes + headerSize - session.initialBudget(), 0);
- willPayloadDeferred = deferred;
+ willMessageBuffer.putBytes(will.limit(), payload.buffer(), payload.offset(), payload.limit());
- final MqttDataExFW.Builder sessionDataExBuilder =
- mqttSessionDataExRW.wrap(sessionExtBuffer, 0, sessionExtBuffer.capacity())
- .typeId(mqttTypeId)
- .session(s -> s.deferred(deferred).kind(k -> k.set(MqttSessionDataKind.WILL)));
+ int flags = willPayloadBytes + headerSize > session.initialBudget() ? FLAG_INIT : FLAG_INIT | FLAG_FIN;
+ int deferred = Math.max(willPayloadBytes + headerSize - session.initialBudget(), 0);
+ willPayloadDeferred = deferred;
- publishedWillSize = session.doSessionData(traceId, flags,
- willMessageBuffer, 0, headerSize + payload.sizeof(), headerSize, sessionDataExBuilder.build());
+ final MqttDataExFW.Builder sessionDataExBuilder =
+ mqttSessionDataExRW.wrap(sessionExtBuffer, 0, sessionExtBuffer.capacity())
+ .typeId(mqttTypeId)
+ .session(s -> s.deferred(deferred).kind(k -> k.set(MqttSessionDataKind.WILL)));
- if (publishedWillSize < headerSize)
- {
- willPayloadDeferred = 0;
- }
+ publishedWillSize = session.doSessionData(traceId, flags,
+ willMessageBuffer, 0, headerSize + payload.sizeof(), headerSize, sessionDataExBuilder.build());
- willPayloadBytes -= payloadSize;
- progress += payloadSize;
- }
- else
+ if (publishedWillSize < headerSize)
{
- final OctetsFW payload = payloadRO.wrap(buffer, offset, limit);
- assert willPayloadDeferred >= 0;
- int flags = willPayloadDeferred - payload.sizeof() > 0 ? FLAG_CONT : FLAG_FIN;
-
- publishedWillSize = session.doSessionData(traceId, flags,
- payload.buffer(), offset, limit, 0, EMPTY_OCTETS);
- willPayloadDeferred -= publishedWillSize;
- willPayloadBytes -= publishedWillSize;
- progress += publishedWillSize;
+ willPayloadDeferred = 0;
}
+
+ willPayloadBytes -= payloadSize;
+ progress += payloadSize;
+ }
+ else
+ {
+ final OctetsFW payload = payloadRO.wrap(buffer, offset, limit);
+ assert willPayloadDeferred >= 0;
+ int flags = willPayloadDeferred - payload.sizeof() > 0 ? FLAG_CONT : FLAG_FIN;
+
+ publishedWillSize = session.doSessionData(traceId, flags,
+ payload.buffer(), offset, limit, 0, EMPTY_OCTETS);
+ willPayloadDeferred -= publishedWillSize;
+ willPayloadBytes -= publishedWillSize;
+ progress += publishedWillSize;
}
}
+
return progress;
}
@@ -3268,7 +3269,7 @@ private void onDecodePublishPayload(
if (publishPayloadDeferred == 0)
{
- publishPayloadDeferred = publishPayloadBytes - length;
+ publishPayloadDeferred = decodeablePublishPayloadBytes - length;
final Flyweight dataEx = mqttPublishDataExRW.wrap(dataExtBuffer, 0, dataExtBuffer.capacity())
.typeId(mqttTypeId)
.publish(p ->
@@ -3294,7 +3295,7 @@ private void onDecodePublishPayload(
{
stream.doPublishData(traceId, authorization, reserved, packetId, payload, dataFlags,
offset, limit, dataEx);
- publishPayloadBytes -= length;
+ decodeablePublishPayloadBytes -= length;
}
}
else
@@ -3307,13 +3308,13 @@ private void onDecodePublishPayload(
{
stream.doPublishData(traceId, authorization, reserved, packetId, payload, dataFlags,
offset, limit, EMPTY_OCTETS);
- publishPayloadBytes -= length;
+ decodeablePublishPayloadBytes -= length;
}
}
}
else
{
- publishPayloadBytes -= length;
+ decodeablePublishPayloadBytes -= length;
}
}
}
@@ -6011,7 +6012,7 @@ private void onPublishExpiredSignal(
final long traceId = signal.traceId();
final long now = System.currentTimeMillis();
- if (now >= publishExpiresAt && publishPayloadBytes == 0)
+ if (now >= publishExpiresAt && decodeablePublishPayloadBytes == 0)
{
doPublishAppEnd(traceId);
}
@@ -6133,7 +6134,7 @@ private void cleanupAbort(
private int initialBudget()
{
- return initialMax - (int)(initialSeq - initialAck) - initialPad;
+ return initialMax - (int)(initialSeq - initialAck);
}
}
diff --git a/runtime/binding-openapi-asyncapi/pom.xml b/runtime/binding-openapi-asyncapi/pom.xml
index 200845e8bd..daf70d5f85 100644
--- a/runtime/binding-openapi-asyncapi/pom.xml
+++ b/runtime/binding-openapi-asyncapi/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
@@ -44,37 +44,30 @@
io.aklivity.zilla
binding-openapi
- ${project.version}
io.aklivity.zilla
binding-asyncapi
- ${project.version}
io.aklivity.zilla
binding-http-kafka
- ${project.version}
io.aklivity.zilla
binding-mqtt-kafka
- ${project.version}
io.aklivity.zilla
binding-http
- ${project.version}
io.aklivity.zilla
binding-mqtt
- ${project.version}
io.aklivity.zilla
binding-kafka
- ${project.version}
${project.groupId}
diff --git a/runtime/binding-openapi-asyncapi/src/test/java/io/aklivity/zilla/runtime/binding/openapi/asyncapi/internal/config/OpenapiAsyncapiConditionConfigAdapterTest.java b/runtime/binding-openapi-asyncapi/src/test/java/io/aklivity/zilla/runtime/binding/openapi/asyncapi/internal/config/OpenapiAsyncapiConditionConfigAdapterTest.java
index 3dc345e468..ede2be2eac 100644
--- a/runtime/binding-openapi-asyncapi/src/test/java/io/aklivity/zilla/runtime/binding/openapi/asyncapi/internal/config/OpenapiAsyncapiConditionConfigAdapterTest.java
+++ b/runtime/binding-openapi-asyncapi/src/test/java/io/aklivity/zilla/runtime/binding/openapi/asyncapi/internal/config/OpenapiAsyncapiConditionConfigAdapterTest.java
@@ -14,10 +14,10 @@
*/
package io.aklivity.zilla.runtime.binding.openapi.asyncapi.internal.config;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
diff --git a/runtime/binding-openapi/pom.xml b/runtime/binding-openapi/pom.xml
index 61a5d840b2..b0aa505914 100644
--- a/runtime/binding-openapi/pom.xml
+++ b/runtime/binding-openapi/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
@@ -44,32 +44,26 @@
io.aklivity.zilla
binding-http
- ${project.version}
io.aklivity.zilla
binding-tcp
- ${project.version}
io.aklivity.zilla
binding-tls
- ${project.version}
io.aklivity.zilla
catalog-inline
- ${project.version}
io.aklivity.zilla
model-core
- ${project.version}
io.aklivity.zilla
model-json
- ${project.version}
${project.groupId}
diff --git a/runtime/binding-proxy/pom.xml b/runtime/binding-proxy/pom.xml
index 6d59876b7d..539c7bc1a9 100644
--- a/runtime/binding-proxy/pom.xml
+++ b/runtime/binding-proxy/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-sse-kafka/pom.xml b/runtime/binding-sse-kafka/pom.xml
index 44bec20351..d3141f3dc4 100644
--- a/runtime/binding-sse-kafka/pom.xml
+++ b/runtime/binding-sse-kafka/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-sse/pom.xml b/runtime/binding-sse/pom.xml
index d2bcef0790..933cd8ff33 100644
--- a/runtime/binding-sse/pom.xml
+++ b/runtime/binding-sse/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-tcp/pom.xml b/runtime/binding-tcp/pom.xml
index bfd883d530..c5a3b8bb7c 100644
--- a/runtime/binding-tcp/pom.xml
+++ b/runtime/binding-tcp/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/binding-tcp/src/main/java/io/aklivity/zilla/runtime/binding/tcp/internal/stream/TcpClientRouter.java b/runtime/binding-tcp/src/main/java/io/aklivity/zilla/runtime/binding/tcp/internal/stream/TcpClientRouter.java
index 41ef1b2469..86feb20b3e 100644
--- a/runtime/binding-tcp/src/main/java/io/aklivity/zilla/runtime/binding/tcp/internal/stream/TcpClientRouter.java
+++ b/runtime/binding-tcp/src/main/java/io/aklivity/zilla/runtime/binding/tcp/internal/stream/TcpClientRouter.java
@@ -306,6 +306,8 @@ private InetSocketAddress resolveInetSocketAddress(
destinationInet6.buffer().getBytes(destinationInet6.offset(), ipv6);
resolved = new InetSocketAddress(InetAddress.getByAddress(ipv6), destinationPortInet6);
break;
+ default:
+ break;
}
}
catch (UnknownHostException e)
@@ -318,6 +320,8 @@ private InetSocketAddress resolveInetSocketAddress(
private static final class TcpDnsFailedException extends RuntimeException
{
+ private static final long serialVersionUID = 1L;
+
private final String hostname;
TcpDnsFailedException(
diff --git a/runtime/binding-tls/pom.xml b/runtime/binding-tls/pom.xml
index bf05af4943..157ad6acb1 100644
--- a/runtime/binding-tls/pom.xml
+++ b/runtime/binding-tls/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
@@ -47,18 +47,6 @@
${project.version}
test
-
- ${project.groupId}
- vault-filesystem
- ${project.version}
- test
-
-
- ${project.groupId}
- binding-echo
- ${project.version}
- test
-
junit
junit
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfiguration.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfiguration.java
index c88108b9de..a1c4f736a7 100644
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfiguration.java
+++ b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfiguration.java
@@ -17,18 +17,12 @@
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_VERBOSE;
-import java.security.KeyStore;
-
import io.aklivity.zilla.runtime.engine.Configuration;
public class TlsConfiguration extends Configuration
{
public static final IntPropertyDef TLS_HANDSHAKE_WINDOW_BYTES;
public static final IntPropertyDef TLS_HANDSHAKE_TIMEOUT;
- public static final PropertyDef TLS_KEY_MANAGER_ALGORITHM;
- public static final PropertyDef TLS_CACERTS_STORE_TYPE;
- public static final PropertyDef TLS_CACERTS_STORE;
- public static final PropertyDef TLS_CACERTS_STORE_PASS;
public static final BooleanPropertyDef TLS_IGNORE_EMPTY_VAULT_REFS;
public static final LongPropertyDef TLS_AWAIT_SYNC_CLOSE_MILLIS;
public static final BooleanPropertyDef TLS_PROACTIVE_CLIENT_REPLY_BEGIN;
@@ -41,10 +35,6 @@ public class TlsConfiguration extends Configuration
final ConfigurationDef config = new ConfigurationDef("zilla.binding.tls");
TLS_HANDSHAKE_WINDOW_BYTES = config.property("handshake.window.bytes", 65536);
TLS_HANDSHAKE_TIMEOUT = config.property("handshake.timeout", 10);
- TLS_KEY_MANAGER_ALGORITHM = config.property("handshake.key.manager.algorithm", "PKIX");
- TLS_CACERTS_STORE_TYPE = config.property("cacerts.store.type", TlsConfiguration::cacertsStoreTypeDefault);
- TLS_CACERTS_STORE = config.property("cacerts.store", TlsConfiguration::cacertsStoreDefault);
- TLS_CACERTS_STORE_PASS = config.property("cacerts.store.pass");
TLS_IGNORE_EMPTY_VAULT_REFS = config.property("ignore.empty.vault.refs", false);
TLS_AWAIT_SYNC_CLOSE_MILLIS = config.property("await.sync.close.millis", 3000L);
TLS_PROACTIVE_CLIENT_REPLY_BEGIN = config.property("proactive.client.reply.begin", false);
@@ -68,26 +58,6 @@ public int handshakeTimeout()
return TLS_HANDSHAKE_TIMEOUT.getAsInt(this);
}
- public String keyManagerAlgorithm()
- {
- return TLS_KEY_MANAGER_ALGORITHM.get(this);
- }
-
- public String cacertsStoreType()
- {
- return TLS_CACERTS_STORE_TYPE.get(this);
- }
-
- public String cacertsStore()
- {
- return TLS_CACERTS_STORE.get(this);
- }
-
- public String cacertsStorePass()
- {
- return TLS_CACERTS_STORE_PASS.get(this);
- }
-
public boolean ignoreEmptyVaultRefs()
{
return TLS_IGNORE_EMPTY_VAULT_REFS.getAsBoolean(this);
@@ -108,18 +78,6 @@ public boolean verbose()
return TLS_VERBOSE.getAsBoolean(this);
}
- private static String cacertsStoreTypeDefault(
- Configuration config)
- {
- return System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
- }
-
- private static String cacertsStoreDefault(
- Configuration config)
- {
- return System.getProperty("javax.net.ssl.trustStore");
- }
-
private static boolean verboseDefault(
Configuration config)
{
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventContext.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventContext.java
index 385df6c450..1ed0cdb060 100644
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventContext.java
+++ b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventContext.java
@@ -18,11 +18,8 @@
import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_FAILED;
import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_HANDSHAKE_FAILED;
import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_KEY_REJECTED;
-import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_KEY_VERIFICATION_FAILED;
import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_PEER_NOT_VERIFIED;
import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventType.TLS_PROTOCOL_REJECTED;
-import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsKeyFailureType.TLS_KEY_INVALID;
-import static io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsKeyFailureType.TLS_KEY_MISSING;
import java.nio.ByteBuffer;
import java.time.Clock;
@@ -32,7 +29,6 @@
import io.aklivity.zilla.runtime.binding.tls.internal.types.event.EventFW;
import io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventExFW;
-import io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsKeyFailureType;
import io.aklivity.zilla.runtime.engine.EngineContext;
import io.aklivity.zilla.runtime.engine.binding.function.MessageConsumer;
@@ -50,7 +46,6 @@ public class TlsEventContext
private final int tlsKeyRejectedEventId;
private final int tlsPeerNotVerifiedEventId;
private final int tlsHandshakeFailedEventId;
- private final int tlsKeyPairVerificationFailedEventId;
private final MessageConsumer eventWriter;
private final Clock clock;
@@ -63,7 +58,6 @@ public TlsEventContext(
this.tlsKeyRejectedEventId = context.supplyEventId("binding.tls.key.rejected");
this.tlsPeerNotVerifiedEventId = context.supplyEventId("binding.tls.peer.not.verified");
this.tlsHandshakeFailedEventId = context.supplyEventId("binding.tls.handshake.failed");
- this.tlsKeyPairVerificationFailedEventId = context.supplyEventId("binding.tls.key.pair.verification.failed");
this.eventWriter = context.supplyEventWriter();
this.clock = context.clock();
}
@@ -172,42 +166,4 @@ public void tlsHandshakeFailed(
.build();
eventWriter.accept(tlsTypeId, event.buffer(), event.offset(), event.limit());
}
-
- public void tlsKeyPairMissing(
- long bindingId,
- String keyName)
- {
- tlsKeyPairVerificationFailed(TLS_KEY_MISSING, bindingId, keyName);
- }
-
- public void tlsKeyPairInvalid(
- long bindingId,
- String keyName)
- {
- tlsKeyPairVerificationFailed(TLS_KEY_INVALID, bindingId, keyName);
- }
-
- private void tlsKeyPairVerificationFailed(
- TlsKeyFailureType failureType,
- long bindingId,
- String keyName)
- {
- TlsEventExFW extension = tlsEventExRW
- .wrap(extensionBuffer, 0, extensionBuffer.capacity())
- .tlsKeyVerificationFailed(e -> e
- .typeId(TLS_KEY_VERIFICATION_FAILED.value())
- .failureType(t -> t.set(failureType))
- .keyName(keyName)
- )
- .build();
- EventFW event = eventRW
- .wrap(eventBuffer, 0, eventBuffer.capacity())
- .id(tlsKeyPairVerificationFailedEventId)
- .timestamp(clock.millis())
- .traceId(0L)
- .namespacedId(bindingId)
- .extension(extension.buffer(), extension.offset(), extension.limit())
- .build();
- eventWriter.accept(tlsTypeId, event.buffer(), event.offset(), event.limit());
- }
}
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventFormatter.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventFormatter.java
index 10dbd107b7..992946d7f6 100644
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventFormatter.java
+++ b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsEventFormatter.java
@@ -19,7 +19,6 @@
import io.aklivity.zilla.runtime.binding.tls.internal.types.event.EventFW;
import io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsEventExFW;
-import io.aklivity.zilla.runtime.binding.tls.internal.types.event.TlsKeyVerificationFailedExFW;
import io.aklivity.zilla.runtime.engine.Configuration;
import io.aklivity.zilla.runtime.engine.event.EventFormatterSpi;
@@ -69,16 +68,6 @@ public String format(
result = "The client and server could not negotiate the desired level of security.";
break;
}
- case TLS_KEY_VERIFICATION_FAILED:
- {
- TlsKeyVerificationFailedExFW ex = extension.tlsKeyVerificationFailed();
- result = switch (ex.failureType().get())
- {
- case TLS_KEY_MISSING -> String.format("Key (%s) is missing.", ex.keyName());
- case TLS_KEY_INVALID -> String.format("Key (%s) is invalid.", ex.keyName());
- };
- break;
- }
}
return result;
}
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsBindingConfig.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsBindingConfig.java
index 2fafaa55b3..a2bca55814 100644
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsBindingConfig.java
+++ b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsBindingConfig.java
@@ -25,10 +25,7 @@
import static javax.net.ssl.StandardConstants.SNI_HOST_NAME;
import java.security.KeyStore;
-import java.security.KeyStore.TrustedCertificateEntry;
import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -45,7 +42,6 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
-import javax.security.auth.x500.X500Principal;
import org.agrona.LangUtil;
@@ -60,12 +56,11 @@
import io.aklivity.zilla.runtime.binding.tls.internal.types.stream.ProxyBeginExFW;
import io.aklivity.zilla.runtime.engine.config.BindingConfig;
import io.aklivity.zilla.runtime.engine.config.KindConfig;
+import io.aklivity.zilla.runtime.engine.security.Trusted;
import io.aklivity.zilla.runtime.engine.vault.VaultHandler;
public final class TlsBindingConfig
{
- private static final String TYPE_DEFAULT = "PKCS12";
-
private static final TlsOptionsConfig OPTIONS_DEFAULT = TlsOptionsConfig.builder().build();
public final long id;
@@ -94,20 +89,15 @@ public void init(
VaultHandler vault,
SecureRandom random)
{
- TlsKeyPairVerifier verifier = new TlsKeyPairVerifier();
- char[] keysPass = "generated".toCharArray();
- KeyStore keys = newKeys(config, vault, keysPass, verifier, events, options.keys, options.signers);
- KeyStore trust = newTrust(config, vault, options.trust, options.trustcacerts && kind == KindConfig.CLIENT);
+ KeyManagerFactory keys = newKeys(config, vault, options.keys, options.signers);
+ TrustManagerFactory trust = newTrust(config, vault, options.trust, options.trustcacerts && kind == KindConfig.CLIENT);
try
{
KeyManager[] keyManagers = null;
if (keys != null)
{
- String keyManagerAlgorithm = config.keyManagerAlgorithm();
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgorithm);
- keyManagerFactory.init(keys, keysPass);
- keyManagers = keyManagerFactory.getKeyManagers();
+ keyManagers = keys.getKeyManagers();
if (keyManagers != null && kind == KindConfig.CLIENT)
{
@@ -125,10 +115,7 @@ public void init(
TrustManager[] trustManagers = null;
if (trust != null)
{
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init(trust);
- trustManagers = trustManagerFactory.getTrustManagers();
+ trustManagers = trust.getTrustManagers();
}
String version = options.version != null ? options.version : "TLS";
@@ -406,16 +393,13 @@ private String selectAlpn(
return selected;
}
- private KeyStore newKeys(
+ private KeyManagerFactory newKeys(
TlsConfiguration config,
VaultHandler vault,
- char[] password,
- TlsKeyPairVerifier verifier,
- TlsEventContext events,
List keyNames,
List signerNames)
{
- KeyStore store = null;
+ KeyManagerFactory keys = null;
keys:
try
@@ -425,64 +409,23 @@ private KeyStore newKeys(
break keys;
}
- if (config.ignoreEmptyVaultRefs())
- {
- keyNames = ignoreEmptyNames(keyNames);
- signerNames = ignoreEmptyNames(signerNames);
- }
-
- if (keyNames != null || signerNames != null)
- {
- store = KeyStore.getInstance(TYPE_DEFAULT);
- store.load(null, password);
- }
-
if (keyNames != null)
{
- assert store != null;
-
- for (String keyName : keyNames)
+ if (config.ignoreEmptyVaultRefs())
{
- KeyStore.PrivateKeyEntry entry = vault.key(keyName);
- if (entry == null)
- {
- events.tlsKeyPairMissing(this.id, keyName);
- continue;
- }
- boolean valid = verifier.verify(entry);
- if (!valid)
- {
- events.tlsKeyPairInvalid(this.id, keyName);
- continue;
- }
- KeyStore.ProtectionParameter protection = new KeyStore.PasswordProtection(password);
- store.setEntry(keyName, entry, protection);
+ keyNames = ignoreEmptyNames(keyNames);
}
- }
- if (signerNames != null)
+ keys = vault.initKeys(keyNames);
+ }
+ else if (signerNames != null)
{
- assert store != null;
-
- for (String signerName : signerNames)
+ if (config.ignoreEmptyVaultRefs())
{
- KeyStore.PrivateKeyEntry[] entries = vault.keys(signerName);
- if (entries != null)
- {
- for (KeyStore.PrivateKeyEntry entry : entries)
- {
- KeyStore.ProtectionParameter protection = new KeyStore.PasswordProtection(password);
- Certificate certificate = entry.getCertificate();
- if (certificate instanceof X509Certificate)
- {
- X509Certificate x509 = (X509Certificate) certificate;
- X500Principal x500 = x509.getSubjectX500Principal();
- String alias = String.format("%s %d", x500.getName(), x509.getSerialNumber());
- store.setEntry(alias, entry, protection);
- }
- }
- }
+ signerNames = ignoreEmptyNames(signerNames);
}
+
+ keys = vault.initSigners(signerNames);
}
}
catch (Exception ex)
@@ -490,16 +433,16 @@ private KeyStore newKeys(
LangUtil.rethrowUnchecked(ex);
}
- return store;
+ return keys;
}
- private KeyStore newTrust(
+ private TrustManagerFactory newTrust(
TlsConfiguration config,
VaultHandler vault,
List trustNames,
boolean trustcacerts)
{
- KeyStore store = null;
+ TrustManagerFactory trust = null;
try
{
@@ -508,34 +451,17 @@ private KeyStore newTrust(
trustNames = ignoreEmptyNames(trustNames);
}
- if (trustNames != null || trustcacerts)
- {
- store = KeyStore.getInstance(TYPE_DEFAULT);
- store.load(null, null);
- }
+ KeyStore cacerts = trustcacerts ? Trusted.cacerts(config) : null;
- if (vault != null && trustNames != null)
+ if (vault != null)
{
- for (String trustName : trustNames)
- {
- KeyStore.TrustedCertificateEntry entry = vault.certificate(trustName);
-
- store.setEntry(trustName, entry, null);
- }
+ trust = vault.initTrust(trustNames, cacerts);
}
-
- if (trustcacerts)
+ else if (cacerts != null)
{
- TrustedCertificateEntry[] cacerts = TlsTrust.cacerts(config);
-
- for (TrustedCertificateEntry cacert : cacerts)
- {
- X509Certificate trusted = (X509Certificate) cacert.getTrustedCertificate();
- X500Principal subject = trusted.getSubjectX500Principal();
- String name = subject.getName();
-
- store.setCertificateEntry(name, trusted);
- }
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(cacerts);
+ trust = factory;
}
}
catch (Exception ex)
@@ -543,7 +469,7 @@ private KeyStore newTrust(
LangUtil.rethrowUnchecked(ex);
}
- return store;
+ return trust;
}
private List ignoreEmptyNames(
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifier.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifier.java
deleted file mode 100644
index 03a800810c..0000000000
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifier.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2021-2023 Aklivity Inc.
- *
- * Aklivity licenses this file to you 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:
- *
- * http://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 io.aklivity.zilla.runtime.binding.tls.internal.config;
-
-import static org.agrona.LangUtil.rethrowUnchecked;
-
-import java.security.InvalidKeyException;
-import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.concurrent.ThreadLocalRandom;
-
-public class TlsKeyPairVerifier
-{
- private static final String ALGORITHM = "SHA256withRSA";
-
- public boolean verify(
- KeyStore.PrivateKeyEntry entry)
- {
- boolean valid = false;
- try
- {
- PrivateKey privateKey = entry.getPrivateKey();
- PublicKey publicKey = entry.getCertificate().getPublicKey();
-
- // create a challenge
- byte[] challenge = new byte[10000];
- ThreadLocalRandom.current().nextBytes(challenge);
-
- // sign using the private key
- Signature sig = Signature.getInstance(ALGORITHM);
- sig.initSign(privateKey);
- sig.update(challenge);
- byte[] signature = sig.sign();
-
- // verify signature using the public key
- sig.initVerify(publicKey);
- sig.update(challenge);
- valid = sig.verify(signature);
- }
- catch (InvalidKeyException | SignatureException ex)
- {
- // key invalid
- }
- catch (NoSuchAlgorithmException ex)
- {
- rethrowUnchecked(ex);
- }
- return valid;
- }
-}
diff --git a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrust.java b/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrust.java
deleted file mode 100644
index 0528234984..0000000000
--- a/runtime/binding-tls/src/main/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrust.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2021-2023 Aklivity Inc.
- *
- * Aklivity licenses this file to you 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:
- *
- * http://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 io.aklivity.zilla.runtime.binding.tls.internal.config;
-
-import static java.util.Collections.list;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.KeyStore.TrustedCertificateEntry;
-import java.util.LinkedList;
-import java.util.List;
-
-import io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration;
-
-public final class TlsTrust
-{
- private TlsTrust()
- {
- }
-
- public static TrustedCertificateEntry[] cacerts(
- TlsConfiguration config)
- {
- String storeType = config.cacertsStoreType();
- String store = config.cacertsStore();
- String storePass = config.cacertsStorePass();
-
- TrustedCertificateEntry[] certificates = null;
-
- if (store == null || !Files.exists(Paths.get(store)))
- {
- String home = System.getProperty("java.home");
-
- store = String.format("%s/lib/security/jssecacerts", home);
-
- if (!Files.exists(Paths.get(store)))
- {
- store = String.format("%s/lib/security/cacerts", home);
-
- if (!Files.exists(Paths.get(store)))
- {
- store = null;
- }
- }
- }
-
- if (store != null)
- {
- try
- {
- KeyStore cacerts = KeyStore.getInstance(storeType);
- cacerts.load(new FileInputStream(store), storePass != null ? storePass.toCharArray() : null);
-
- List trusted = new LinkedList<>();
- for (String alias : list(cacerts.aliases()))
- {
- if (cacerts.isCertificateEntry(alias))
- {
- TrustedCertificateEntry entry = (TrustedCertificateEntry) cacerts.getEntry(alias, null);
- trusted.add(entry);
- }
- }
-
- certificates = trusted.toArray(TrustedCertificateEntry[]::new);
- }
- catch (GeneralSecurityException | IOException ex)
- {
- // unable to load
- }
- }
-
- return certificates;
- }
-}
diff --git a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfigurationTest.java b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfigurationTest.java
index deaa584caf..ed6647312d 100644
--- a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfigurationTest.java
+++ b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/TlsConfigurationTest.java
@@ -15,9 +15,6 @@
*/
package io.aklivity.zilla.runtime.binding.tls.internal;
-import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration.TLS_CACERTS_STORE;
-import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration.TLS_CACERTS_STORE_PASS;
-import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration.TLS_CACERTS_STORE_TYPE;
import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration.TLS_HANDSHAKE_TIMEOUT;
import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration.TLS_HANDSHAKE_WINDOW_BYTES;
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_TASK_PARALLELISM;
@@ -27,9 +24,6 @@
public class TlsConfigurationTest
{
- public static final String TLS_CACERTS_STORE_TYPE_NAME = "zilla.binding.tls.cacerts.store.type";
- public static final String TLS_CACERTS_STORE_NAME = "zilla.binding.tls.cacerts.store";
- public static final String TLS_CACERTS_STORE_PASS_NAME = "zilla.binding.tls.cacerts.store.pass";
public static final String TLS_HANDSHAKE_WINDOW_BYTES_NAME = "zilla.binding.tls.handshake.window.bytes";
public static final String TLS_HANDSHAKE_TIMEOUT_NAME = "zilla.binding.tls.handshake.timeout";
public static final String ENGINE_TASK_PARALLELISM_NAME = "zilla.engine.task.parallelism";
@@ -37,9 +31,6 @@ public class TlsConfigurationTest
@Test
public void shouldVerifyConstants() throws Exception
{
- assertEquals(TLS_CACERTS_STORE_TYPE.name(), TLS_CACERTS_STORE_TYPE_NAME);
- assertEquals(TLS_CACERTS_STORE.name(), TLS_CACERTS_STORE_NAME);
- assertEquals(TLS_CACERTS_STORE_PASS.name(), TLS_CACERTS_STORE_PASS_NAME);
assertEquals(TLS_HANDSHAKE_WINDOW_BYTES.name(), TLS_HANDSHAKE_WINDOW_BYTES_NAME);
assertEquals(TLS_HANDSHAKE_TIMEOUT.name(), TLS_HANDSHAKE_TIMEOUT_NAME);
assertEquals(ENGINE_TASK_PARALLELISM.name(), ENGINE_TASK_PARALLELISM_NAME);
diff --git a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/bench/TlsHandshakeBM.java b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/bench/TlsHandshakeBM.java
index a1bcf842db..80f1880cb0 100644
--- a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/bench/TlsHandshakeBM.java
+++ b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/bench/TlsHandshakeBM.java
@@ -51,7 +51,7 @@
import io.aklivity.zilla.runtime.engine.binding.BindingHandler;
import io.aklivity.zilla.runtime.engine.binding.function.MessageConsumer;
import io.aklivity.zilla.runtime.engine.config.NamespaceConfig;
-import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemOptionsConfig;
+import io.aklivity.zilla.runtime.engine.test.internal.vault.config.TestVaultOptionsConfig;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@@ -84,17 +84,148 @@ public void init()
.vault()
.name("server")
.type("filesystem")
- .options(FileSystemOptionsConfig.builder()
- .keys()
- .store("stores/server/keys")
- .type("pkcs12")
- .password("generated")
- .build()
- .trust()
- .store("stores/client/trust")
- .type("pkcs12")
- .password("generated")
- .build()
+ .options(TestVaultOptionsConfig.builder()
+ .key("localhost",
+ """
+ -----BEGIN PRIVATE KEY-----
+ MIIJRQIBADANBgkqhkiG9w0BAQEFAASCCS8wggkrAgEAAoICAQDPk6zIt8JoQLQQ
+ AIYYws3lJAq6tBjPFTuuqgvly/EelozPzW4ycbpXXrpGSe3EU7tBBcGuv81gO3UX
+ VlYGQQroY1kXB1khZM8YTBcCZMq0ByunmKRF6nGxhHPDu7qmQhreRDioodsxiN0I
+ PxCjgxBKTFniF3hFVG8btPB6WMr39oyHGon0RlMqV92ia8c+XFnTHWyqVc10KhSA
+ WNpeF7Boic1QF7oi5EWVz2bJbI1tOSeUz8C1OlUhb9r8/zFvacJSIdCwhOtuHHJu
+ 1pSdIiOYzMtJfXWkueN2hc46tZQ3ZoyD27u8GMkk1dA1tPH9ouZlS13UoxcinOuE
+ ffMiONrT2miAmeqGQwbyNENNcdyMec4+fWJyft01+zNf5w5ybZgpYy5N8H5tVjwi
+ cOq9Mx+atBxVimaRmj9wh0PofqLgpaxrXHk8ToiMAkoveIsdjFq/JXrV2RvVPaU4
+ YeivwZiA9DTZ1USRPU3YA/zYTf+Dm/cBxEK9wMfygQoP8zaO3RWNFe4Ulpw883r5
+ O4fyhxvVVIngUcoVXi2wfBanW3ErnvYZVJ9RuZHiFnB0jzBhMuj0yedgYnuopMzY
+ EjIfXtf5ep5k0pMPLQYCQ9Wp9ge9txbWy2V0wZJUFBX7yMFs1/gFG/hxxD4w40Uu
+ xcIDG+26LrY9HfOA7tEB+GA/LRZjkQIDAQABAoICAQDG+KMS8zHihMMU46umaHS8
+ REQUmzV8qrm+vzkQWOETlPP87MnIiMM5pI+heJP1MN25gi8ZtrjCmbuvVw62h/pQ
+ r4piTKTfIaZxf94+aSb0UjtCN0qfyg6ZPoFJCdXsMElY7MPywNM/NBXLJchpM+SV
+ k4JE+oJK0ph+Un6AiERmU2p4xrOd9xsY54iHfBBMcnGXsAjNbdm2k+9657DJqKNs
+ UUsAjv3ZHD6nT1sTkH4wSCzstAfgr72Sg4nCIUvdo96ko81Kpt6VeacnH4Ds7iB5
+ AzWJiD0QXS7wGWqJVCxyvlXKvwHBV3DXYixmjr+3hEKcrhWPEZXHk+sd4S0BjMYT
+ up7mj2GmVSTwL/XS7cCVk4QS4901P2xhnINrw40W40JWinFJ3mW2Z4oySTNsWy0N
+ hugHlw+O0fRFPlVwh9uu8fpLIJQAbO0XwnJ2y7G1dkvFzTEtAxHyVanfwvepwAHl
+ +pszn/LsvAcLO9ncWUvt2pGm4Zz3sAHDYHoTEUUFvJfF33m49Vdgl9SeHVcAIbXc
+ XOnaw8NLoxyR4Q+jtvlr1yFF8Kt3zrA02Bj9W2TIyWJdbwJLjfTK7X4l9Bzin1Ec
+ 17r4XCEsWSKQ9E19AmSriNoGLczhfDCr26BQDVLCwmnKmF86CWrL53PZ9iEXEEVA
+ yruQItpmIRsG4Fu5FKkNAQKCAQEA7Iom5vVMt0sibUYozumEQJ/FdG1vyIn5a3tj
+ ROqGExFy9fwAQi734a45VuooUw7ot9kaGaGrqy6l2a0EyxJNUnF90cuvfX/ZrFwk
+ /w9fWc0TSOFlEVCraSghmZ9TsGAg9NyhGQa1HNrq4XnR9LSWftyOWr6GGUJeufdn
+ qs/gpBBDtMVIAbusbtxuEAMtIfbuc3vvhP0HdTmeXAnUcgQCC+kPZTh/iE0YhCdZ
+ WKxRWoj5HhSMrWvmL/29G3/qN8eMzz0aZLjCweCi8lji69QIVyf90y0MAQ0e20EC
+ n+HX7gzdAvpS+K1I+lMYh9PivcYjiO3fVPMoHUCd6EiKiEy1GQKCAQEA4KeG3/Z9
+ PzSgNWq0HZi62CM/ixFNxgxsPKFxK4zntOAIQIihPR8CpT3Z5UeD4SbP7f5Jep49
+ 7y7zc5CaIb0+bNVSdoSIp/d6/0iNgXwhJEkP3JIpWbhlT/K1OF8V2ItvOFfBkMYl
+ WmHH+V1i00CQAthczmrdYMWCr38S+Pfbcz1ywNyLH1Unh4xo1USXADgpISfLapDH
+ AlUexIyJE3X8XurR0h7BIiAc8oyRfPVEtyASz/aDxLVrG9HpSWS8fsMcQWUKHGBa
+ KrK0sFi7Tc9OPCnLU6dnCT6cLM0qqGGLqqM8Cmmcz+HdhHAAt5XBn53HhERvM1og
+ VtlNosVEl5u5OQKCAQEAnx0roA8ARQgop2Mbjlws15/iHjiDil2txyxgEXrFJ8yE
+ DY4vylV373rYHWw0JfMQfqNu2DEVnngpnmyxnby5AK1RWq/uY7h9/2CYjm6T0H+P
+ 6mWcK/Kc64bQW1t+21U+thg30fLeIAPvHi9pGXflCH9qzwX8hL9No0EWniNp1FMQ
+ iGhw0KGjE4v6CZFpacCGlG6ZJ1diDevtZ7JBE1U43zQuZAOGXnSl+jfR9UEtFH6x
+ PRfLrdi4Ji5EaFw6fL0iLkHHIFvcvrhSRD2gn8yos6A2MTjLK2XdDNYFYvFz5DEI
+ 9rjW2WsPfTwqcywICWpgevqwUZ+jq1HNJvStI5Sd+QKCAQEAnAHaeNcMXQMnqlCi
+ KddxET1RGDr4/mqME4KtO2gLVCErudzn07EgPi34je8e1xED3irzTfJr4hiBuaQW
+ VQ29Nwjgzir1V2dWA8eXdO8FeNQ/7pWVn5ecy2spi68EVa9mmgLfCbGAKQa0Pygp
+ w7gXCdLEiBfQCi6+tS6St1AwFhP7B5FgD28sF0ZbWpbaIa3eagbfjO5jNOx1hFpv
+ qpMJocSB1t/CkPcqAwm40sTkZiMgzUhMcyLk8ZnQ3kXVXFYT3hnTbqc+ll4pejj0
+ QXGPy9neOAaNV+8htz72u52ZxvK6dCSpX/dixGCfLt4Ras2/ystXSZrx0D3xWvKQ
+ 0pOyiQKCAQEAtGU0KDx5aC+zpf1Jd6xd9jPoKY9OBLIuwp1BL7Oio1d7JKrxleOt
+ zjtZrxZXFDdFDUeOCPy4xWv/zblbVIRhp93ruQX/j5bDZvWo85aozTf80t2+aZgr
+ 5msAz3/UBQ5GY0ASJreJlbuSfPn5s9fLBbHNgFg29cmqjxPKU8vUiHXNxcBbRhpU
+ Mp/Kplij+O6hKJeqECq5610CgqLg24vPLQKDsXtC2+ZAHvDJzShN8nu1FQnsqmsj
+ xuxpFoXU+KgeFwOIHD2SkB7w2kmvHQ2TUnvqbcWxcA6F2o0U9uEAAWYE7iq6V1hD
+ HiTCqXKPrP1QCU1/TXWVjEgnVnlruNTahA==
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIFYDCCA0gCCQDYZ1VzcCw3pjANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJV
+ UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREwDwYD
+ VQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMMB1Rl
+ c3QgQ0EwHhcNMjExMjIxMjMwNDE0WhcNMzExMjE5MjMwNDE0WjBzMQswCQYDVQQG
+ EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREw
+ DwYDVQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNVBAMM
+ CWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM+TrMi3
+ wmhAtBAAhhjCzeUkCrq0GM8VO66qC+XL8R6WjM/NbjJxuldeukZJ7cRTu0EFwa6/
+ zWA7dRdWVgZBCuhjWRcHWSFkzxhMFwJkyrQHK6eYpEXqcbGEc8O7uqZCGt5EOKih
+ 2zGI3Qg/EKODEEpMWeIXeEVUbxu08HpYyvf2jIcaifRGUypX3aJrxz5cWdMdbKpV
+ zXQqFIBY2l4XsGiJzVAXuiLkRZXPZslsjW05J5TPwLU6VSFv2vz/MW9pwlIh0LCE
+ 624ccm7WlJ0iI5jMy0l9daS543aFzjq1lDdmjIPbu7wYySTV0DW08f2i5mVLXdSj
+ FyKc64R98yI42tPaaICZ6oZDBvI0Q01x3Ix5zj59YnJ+3TX7M1/nDnJtmCljLk3w
+ fm1WPCJw6r0zH5q0HFWKZpGaP3CHQ+h+ouClrGtceTxOiIwCSi94ix2MWr8letXZ
+ G9U9pThh6K/BmID0NNnVRJE9TdgD/NhN/4Ob9wHEQr3Ax/KBCg/zNo7dFY0V7hSW
+ nDzzevk7h/KHG9VUieBRyhVeLbB8FqdbcSue9hlUn1G5keIWcHSPMGEy6PTJ52Bi
+ e6ikzNgSMh9e1/l6nmTSkw8tBgJD1an2B723FtbLZXTBklQUFfvIwWzX+AUb+HHE
+ PjDjRS7FwgMb7boutj0d84Du0QH4YD8tFmORAgMBAAEwDQYJKoZIhvcNAQELBQAD
+ ggIBAAjyCVGqLUl1EGpRmAAcwtFi2uy7isW+RoyQFOycY5hQBi83KxQ9jnl2VmfO
+ A3kb1AKlPhCyNMlaW+qTxiwdWtEx3lf6Efm83ePsbwialMGb0ybQRRdvyEOkw5LO
+ Q5TOUI7R5tijZQMb6qxPjOJwkgQRl6iOqIDAZmO1ttnqZgxtWCWpajLtCpWO2nDk
+ fLq5UsEFv5heyheUjtOu9pGRzNNAHFMgtOqsAmH8wOTqjxAf3YtMPSanM+fW738T
+ akd1mFhtSp2YjVDMUggix9IrFcBJTpDZBHQJdeBPVjoslfGtVaTcpFBSzcqboCwL
+ 8eJwoFYqBzekV0ZjSY2Vo0z9d6TkNDptrwZYDk9MgmN1qV3coBBCTRYxRUhA/kqF
+ slO3nb+RlcUVwQCZY9twzO845kRsrwaT/xpcmuMCA7xSvKAPVz8nDOEAaZF4CISv
+ mRa2Td6UWajJ/RB0G4BkTO+fBa68sWyIFOANAenRP2laMCoLqAS2ApORHVaZ3d3x
+ bF7Mf+BG70ukLzGwK/6XPe79xEr8F3X9eBJ0sbTqXrgvNmpKN/qIixdDqa6UQDUr
+ 7g2E6OCYhMgxXmoWAMshYRTBEVlsG6EGn0v6m5IzWAua+Kg5Jur8j8+JEUArsvt8
+ MdoPFL6oo+FNgQrkHwHkiONYd+iuunJTJEeXFQEzpoxNvrc1
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIFXjCCA0YCCQCuorYrG5wG+DANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJV
+ UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREwDwYD
+ VQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMMB1Rl
+ c3QgQ0EwHhcNMjExMjIxMjMwNDExWhcNMzExMjE5MjMwNDExWjBxMQswCQYDVQQG
+ EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREw
+ DwYDVQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMM
+ B1Rlc3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGPVgVO/zd
+ ebwGWujKymJmztWZ5LIaZC+zY1SwKUBUA3+vrtO79ndi6WePiV0a2e7wov/ajFLp
+ mor2RfGSMD8Yb9e98QSqnfy9Q5+ABmxFulgSJNwDjnxugZuk/6MILKMg7AsgqaxK
+ wROSSLcom8b+gkbwXgHm57RKiitXlRM9ujdKibeHwfu7JTk6A7LwRbCVurTRqckw
+ Q0/mA4mNuZ2AMGW+YL36TwTLfTAa4AVHEbI3U5+TyY3DoV7OoHI4Ec1/7B0CGzqK
+ smKM3dKmXpRIc5NBZt+eKqphAhp0CD1eAnutWtepahjWyY1fAYk9hZ+ayU52dAMf
+ +TbkPdMn5jfHhqs95VdfQjsKZPyNTYjhjHN9tAph1wKUG4XRATAvxhA2gpYgN9de
+ 9ztWPboVzGosauQxPrXklO8CF7hsft0RlCCP9ojVLUkZ42vI/M1S3lD8pCDtPe46
+ 2zQ9S3F1R7goF3AqWm4EQqu237+zL45pCbbWyyHeXHeDrv3DNXHcWXoFicNmCBl6
+ nPPsVn9qgdhmJf5QcUKLkJEEtk94Uedv5qEqiJQYSPAIZHKnv4L5Li69kghTbUv/
+ Xquz2JdY5daj5eRurgZVjutkmMIaR4rJdhifBonlcKxoeSZoVbnoGzS5KcF9saz8
+ 9qYU9LtF98CUMY7U4RPlVbA8D4YwDICgcwIDAQABMA0GCSqGSIb3DQEBCwUAA4IC
+ AQDEzoEbCsHpae5M1I+vezg7w5NAItMU4WU9IccTylSB/gfIT+hWwIv9KiqTWjxw
+ Y5Aj6XJ1mATHAMSQnNZCnP2Hw39/Nc3HcKmek2na2zK/TBSEFXudJmox8SK32r26
+ nLstNlcYf7ixqJ5T7SOE2GJOcEUWpvTSbvQD0NvG81BVnSyUfX3FgkQLwwlyBoSE
+ 7FwFz+ybrbisUHHqzPVnSblEDbKv6T9ai3FjbBegzPVSd9RmtB/DzxhdSk+kL1oD
+ VSEPweSHEqamEnq2RIgLb7rYhmfohl0fGF5W6I3LvLqqe0KLRRID9V/jwBUGyICG
+ W3jGu+68jOIUqXA4+gfOwXNktd4F7So48ySbghgrY0Umr4KSs6CTHhvSZ4ZG8QO/
+ ZyC+DjXsU3mihIBP/Q43YU7dYxFSdlCw79YnXvdWu7K7lZ1bIcbdH+RShcbvPcwg
+ iM2qAvCgZBA8xHMDQeev8QdQjxtN+uBfee0mkvbzPbIh/0prywPHjAie/bXVBPVt
+ VK6Gej2egPCIA5ThvGpmXh8kPd5Aqy1J++cmrzfYfPPsbmPGTLI0HFMhUuzIhFbd
+ TzAV/Qj83r722s6f0v3KEEhfi3EZu3bRSGIyxVtebtOLGvEb2PjJrktyVJgivVFX
+ uHHpz76QFOcLy1F962Hfj51NnIROOySyl12JkhPRTlMoiQ==
+ -----END CERTIFICATE-----
+ """)
+ .trust("serverca",
+ """
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ """)
.build())
.build()
.binding()
diff --git a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifierTest.java b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifierTest.java
deleted file mode 100644
index 770fe20ed7..0000000000
--- a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsKeyPairVerifierTest.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright 2021-2023 Aklivity Inc.
- *
- * Aklivity licenses this file to you 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:
- *
- * http://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 io.aklivity.zilla.runtime.binding.tls.internal.config;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-
-import java.io.ByteArrayInputStream;
-import java.security.KeyFactory;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Base64;
-
-import org.junit.Test;
-
-public class TlsKeyPairVerifierTest
-{
- private static final String RSA_PRIVATE_KEY = """
- -----BEGIN PRIVATE KEY-----
- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDp62LciNw3yMp9
- 0jeeGO+kdGmqZUQqRFZlckggahlxZnLfvckSpdw18+FOvybX+T4RT5KsCwWgl6eM
- DnyDZYnfK0mIk5TW6gX0oNOB/uR4n9DauN2+owlC1144nWauH0xi7clR0OU/MHPY
- n+36bSIYLzOyxJYvs0fWU5j+wkl62nUo3yiII+h+tPhYmWCBmSDhXCD0BIoHzCYP
- A5+c60uws9tSuzy2l8qVa41HDOdIru79GVoYP8jXJoGugoQ/uJBLJd1dCh2Egfi9
- TOJZafR2hWkdY6wzfaC68fWhS1GAnzjChoVCa0P6nUNYkj7BH4MDoHNUkrurU6zN
- sG7gK1lLAgMBAAECggEAT7LETzFOHq+J1k6eZn5Gf/it28F+9QutiAjk7C5aFtn5
- /6NQ88qQ+czrEgJswJ8J96nt5jInK60gB8cTw00AKYo9Fox55LN9bfixt5PZ0gNZ
- jHv6CS7RQ/XPA2kVh/Pf+cDcm8SZpuriPYdX9L/kIutKKPlz1jK2Ih7/fKVDldhb
- xffwdxqmexnLwNHseNevQbhZKjjllLWZzn78xaSaHghN8fc5TF7SsgiqYGK079bG
- nC7luO1t5XqQ85nql9W2ddpYP5+AKNDAatmIcJH9Kg9auuvB3qJtRFOTmcyphLo4
- EtHjn8Q1Qh9zj9se0oAxdWYYqj86XvnUgq2VPlfx2QKBgQD8sXLr6/M8rNo2w+8f
- qQ6u24+/2Xq/JwwZHU5C7Jpfl/cvabxgP8W9seD5dBI+jiUyFAM1fk/baYKmr1JE
- hHAGnN9yvxKj5UA0xZXe9AFkp9sz5x3QCR11lIP8bObGOvVr7z6K9DAkh/Ey9bdM
- fbdN5fupfgDr3bGHHcv7ka3xtQKBgQDs+wsUADtK3STvkiV/bI0O6lJNYKJOhcYn
- yzwMfAU18kGEBUcNfiUrkhFJEoRHurC8hWqMebUmHKKZ0R+8vIGv4BejcvEXdLYt
- cwsjs/vpbY0W4lgaZH/V9Qpg7GPdLEhxPj/MVdKY0rp9kB9Tpo4w8n6wlqdnj9uv
- HHPau37+/wKBgBzXQ/6ZV5G9SMqnYkuCyCI8/RMwh0n58u+K4LvStWvjtFq8/rsd
- jDwyaTMPhGWPY79reVJJsGOijz7nE8SuOPsIPJikJkR+je13/7sKrn4GioZKAqUT
- 5UDeSpIs+8n0QL6o98J0TGpe+bCPSvR4BMvnS+n0b7Z7/x8kz3tPDUNhAoGBAJD/
- TZjwR1cYFjhrYHwly+0bXD4x6T1IRqUkidpNq9aFIqcHn6DW1SFinybpqHxG80p2
- C2pmMXtfO/IHbXbKlEMrRutgMbmbVLhcUq2Gu5TozdH5rdSAN2OPKcmB+dxi8vQv
- FVQOEuwky6x2GWTSXOAAD5o2o7kO4Wi0bQKhhCO7AoGAOiexYhpgXtzURWDzlQFv
- nr9HyYRhxpRg97o5InO5uIkcoSPBb7FS4k8tFscz6DBlz92lDpI1dIB6yvku2JfV
- F0MacyZE7wWaeGV5sH053SZr4I5ySpvbB8S8Q8ZOpt/Zfq3kujfj7y4qUt6VPu/M
- 1rAIceW5pPBxbqdlJHx3W6E=
- -----END PRIVATE KEY-----
- """;
-
- private static final String RSA_VALID_CERTIFICATE = """
- -----BEGIN CERTIFICATE-----
- MIIDrTCCApWgAwIBAgIQFt3rAO4w+uwvtcPwINij2DANBgkqhkiG9w0BAQsFADBz
- MQswCQYDVQQGEwJVUzERMA8GA1UECgwIQWtsaXZpdHkxFDASBgNVBAsMC0RldmVs
- b3BtZW50MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQDDAlUZXN0IENBIDEx
- EjAQBgNVBAcMCVBhbG8gQWx0bzAeFw0yMTA3MjIyMjU0NTdaFw0yMjA4MjIyMzU0
- NTdaMBgxFjAUBgNVBAMMDSouZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
- A4IBDwAwggEKAoIBAQDp62LciNw3yMp90jeeGO+kdGmqZUQqRFZlckggahlxZnLf
- vckSpdw18+FOvybX+T4RT5KsCwWgl6eMDnyDZYnfK0mIk5TW6gX0oNOB/uR4n9Da
- uN2+owlC1144nWauH0xi7clR0OU/MHPYn+36bSIYLzOyxJYvs0fWU5j+wkl62nUo
- 3yiII+h+tPhYmWCBmSDhXCD0BIoHzCYPA5+c60uws9tSuzy2l8qVa41HDOdIru79
- GVoYP8jXJoGugoQ/uJBLJd1dCh2Egfi9TOJZafR2hWkdY6wzfaC68fWhS1GAnzjC
- hoVCa0P6nUNYkj7BH4MDoHNUkrurU6zNsG7gK1lLAgMBAAGjgZcwgZQwGAYDVR0R
- BBEwD4INKi5leGFtcGxlLmNvbTAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFNrwPBOM
- SNJjl2B+QtjfJCjK4aIAMB0GA1UdDgQWBBT2vzUtTK6vtN5UHyiCZOFTKvPXXDAO
- BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0G
- CSqGSIb3DQEBCwUAA4IBAQAwHVTQgjVvxlhE80xi7mKLJrRwAC04CysWe+q3WFTN
- xt5PPmT5ZCanL1aFh62UuvzNTyhcJEo/kuYo4PNEEtywMsLEXre4cWBbcFUyS2sM
- z84ikMiG885ZH3ZfdWwH/N/DLL1Ro8+pIxCyxBzgAXIbuDcvhSN8E0SRTTgtOyeJ
- r+xxf6axm340hzyQHUNv3U7g7H2KYZtnbf98criMcbVYHaxpRyPkDTfmzYRkl2nn
- TgUnkZ3b5BI8XFq6vltAmxwcV/8IjKYd9eKazJg/I+mnMYzIpLYTD/UkMHyUMxrC
- sES8isPpB+QNvHC4gVowAAkxnk216r3Ft0od41jTkrQF
- -----END CERTIFICATE-----
- """;
-
- private static final String RSA_INVALID_CERTIFICATE = """
- -----BEGIN CERTIFICATE-----
- MIIDpjCCAo6gAwIBAgIIJWpr8WDPucUwDQYJKoZIhvcNAQELBQAwXjERMA8GA1UE
- AxMIc2VydmVyY2ExFDASBgNVBAsTC0RldmVsb3BtZW50MREwDwYDVQQKEwhBa2xp
- dml0eTETMBEGA1UECBMKQ2FsaWZvcm5pYTELMAkGA1UEBhMCVVMwHhcNMjQwNjI3
- MTA1OTQ5WhcNMjkwNjAxMTA1OTQ5WjBeMRIwEAYDVQQDEwlsb2NhbGhvc3QxFDAS
- BgNVBAsTC0RldmVsb3BtZW50MRAwDgYDVQQKEwdBa2xpdnR5MRMwEQYDVQQIEwpD
- YWxpZm9ybmlhMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
- AQoCggEBAMPa6QJuljN9vVeBHzb2sB+YgL7pFPI9tL24jaB3mlV4PvXwQ2XoZ7Oy
- gR4I23iU9CfvvhHSiI2+9nAGHZgT74GZDaIiTxZNearQ+4aGRhAAENHXRb7YD6gp
- FiIjZ5SWf2+yfcTIPPi1Sp+DLp9L2FiCgCtghaDBc5xdieC24zhq3Rp56tsaQGen
- sEK4qZ+9bAevD7l59FWat7Yts5p/emu3+UNAka+n2zVz+BF0uc9he4NHgHMvd0uq
- GlnTKWQ3zrQ+LUD7nOOq6db2Pb2+FTg+4+0y/hdzwFkuNL9OOmWvsSOPUIJIBpyR
- gwi0+I+MWODoUPYJE1yLIlGJ5t7SWDsCAwEAAaNoMGYwHQYDVR0OBBYEFD2P04GZ
- l36DmMNQmz7auMC7LStQMA4GA1UdDwEB/wQEAwIFoDAUBgNVHREEDTALgglsb2Nh
- bGhvc3QwHwYDVR0jBBgwFoAU4ebi5t0zi0JyZI1SdZrrG9m6CIwwDQYJKoZIhvcN
- AQELBQADggEBAFr0mzsLaa64aO10HnRpU9ppFkRiFiu3peaqrSZd/1mHxacwPTEr
- c4QtaNhB1sVu1zVlzKPQYJ/yRsUD+wSwURud8syEjeieIXu3mytdQMQN/7hIE3KL
- PO38SUk3pN8GU7auvuysT4/xbnZ7J+55UMCKkjcVO17NwVWXT9BbIKmHScLwCuB+
- +5LG02GLU6IWzrWcCsmsjbMiT+xntAHXzJChwx5abuBpg8hHN3Yaz7/K9E1dVzop
- ZhqL8TG4TlhXtUJjd8VxAroyMi0VZSVHTZoqz2Zmkw/CT30rRT7E/I1zy2ZYUYqS
- kuIWfzOfqCZSmGeqzuSE996ndM+FlQ6r0Zs=
- -----END CERTIFICATE-----
- """;
-
- private static final String ECDSA_PRIVATE_KEY = """
- -----BEGIN PRIVATE KEY-----
- MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQCoTi8NqWS3E2tW
- Crwh1p7bfzPgKj1e7gOjk9bb6CQ7osbZqoR/wdhvEcgIRykxkjLvwYaeRAYboq6g
- jIEkz6H8UwMxZvWZ4d2z3ruOATRwlzw4bITwilwY0nXeqkpHDXathNoQqFMpcXOo
- veEBGNyNOjFH77Scqmc8reoQai2pKFmffWc00kcNCYM+y5rxmsV8kvGrOLYalJUn
- TRVh7tpda29+XFEuYWCciHAK1k+FT3QqcvUB56vZv0350J0BaNr3KrnqDX58SsCa
- ub6UlF3P+1rEs5JhrG6nNIUo9ZlFQIQNrhfZtmt86MCd8fF4jZXEsD0sa4MDBNl5
- SeH911ZqOjb3T84WHU43UHBvG4PcvxiJ86MA/+vKdDHBKXI+GWWNoXZoa4fofAIB
- MCYJOIK35deL8Hwj5yEAHz6843WW896xmUJcGmHn0tAuskfBwcLHhovB1nMu1Oiy
- YTIJ0xghj96fu80ts9VstYK7aAPT5cPp0U2TU/ptH1+R0KEfUdcCAwEAAQKCAYAu
- XZ+T1Ws0Dkr/IKT0c4I++NuLMUfH660f9r31xg2ZSj4av+GRqo7cBluDgEsmZ17V
- 8wwJdLb0DQyrmRmI0RSQhTP3e6REeNdRUpZ7x/Qw4lEKQEcdVxiJFA25wlMFIP3l
- Tpiehyil3aXdwjWGzlkQJQxng29Py5f2PPki/YVHuSB7khoJELbXLhw0g/XTAm7O
- Y1LldxGf3/f5JEC0Qo9wtgS4nGkW7Genp+Sp76FnmdxoR1Qc6rxKl+u82w4t0bsx
- 7EAVdsqgqQ3dzr4Er/BRT9n+al4/SPHa1mE1+l5jncQZgNzIQQ42z02SdofQ+hnk
- 2IVmJvhVB9AvJvaKJ6OgIuzlR3PY0giDouNfHteLERe+RUULiUIV1nLgRYEXrDiQ
- ffqB/xIIc7hFK9l4g4oIPUMfhPa4sgIvz79s/ywyLOz68ZsK9N7sbX9KpkAz7rtI
- mtb8dJNLGLrR1e3a1nmGMejxBcfUsgfRzNlI89hzhu3aJQGwtHn/UI7gz9E8i+UC
- gcEAu0p5HaQ43DkiZJn7G/YW3uakDQ5UphWLQ6jWepnKG991HTdsI7sL/VueAroI
- zFDETIstReropSkqySQp6pXeJvc+knKYnm5OEjvJ7UGpAifyF0o8EG5xTXPKqLst
- wbK3btaVPRCptKZ7mLnHNW3RACYBLGmJnlLjpBpHmKQxnQEckB5ecw263ALFPQPR
- 2J3mK2l+SoYG2khMEqIHzVXk1uk9ks03sA6rSNvcp9yj8q/coGP8dQul8vSf5Vlj
- uGILAoHBAOYMrT7pJ1iYdKHy7l1REE/80ws2jFcwsjQ4C4zk9dX7P3xB+fjIacJF
- j07eWTiHVQ3LUWKPDxrPLic+hBIs8sg2hwq23cPFeFGrOF6VhceIEr2ZXjGgKp9A
- THTeAmTNZD4/pzKbRui8qmO5jMUoml4cXcY4K79FdhJzebVHUt+1MIv47iAqYZA6
- ItzJpsbrVwzhL7RygbP/MFJYdjDiv09IB7AyPhU5SAEH0of/QZLD7MmPZ/5bAiJK
- QJualL+a5QKBwEGMkhEevcvNr0zYW3twyJZg0H/FSTkLhz854z7rfkH9FkcQc6eZ
- uluE6it4IsYnalyDxWeYDK5pVxEouAbjRuoKVHr64pFod6iIBmckONuJUYB3Ochi
- bwM1iHM/d4c4XlzLe1Xw9ARG1DEirCb19VUA+B4sHb8ssYFotTAmHzsc/XsvNc08
- u+5uhcuR/6q5sKn29P1uJQ3WidFnpiVmb34MCcHMUzYqHCaW1IZngXXZuPTlqaDp
- X75FgYTKoU0R+wKBwHMkcSoFxJ3BYM8WKlwmkMWYQ4FfQgr54pfkXVOd3bXGVVY6
- J4Vvug90hW/yNjHm+pk25HsyI1tFy1H1JmF6geHX+OtR79lm4vvteP9OU3E1GDwx
- oUWxZuPiaOItpIETlFLbxTG9Klae56GWY3DjC7CC/iSSRMMtXxWJGqezFTXHGI9W
- fsk2rTJlBsH/ZCw36pAVvazRiz2uQl9Uy4NYWmyyHrb/zrcMvo9VfPh4uDdfPQr9
- bg2PO5gyFfhL/JuSSQKBwALFikkxrWjwwIvbazo1LPYFJD93CsPTNXIFm91DhE6y
- J+GDL5lj2C4lpEthkj+rUPeoJ+7c8IN+NNotOg+ZJfSgpw1Vgp/+7IDVNhx0QIYP
- CATuu7sFKGrYUkLjiIGTAOjaXCevdPfOH8hCN/MyJnFRbT4t0KlzMCgfSpr7SSMH
- zyTaY/Ol9Gf4+x1TV1IaoTPyS0ELdWmLjWdDsbbbE1fyDRtTyE/9iCk6mbbyCuDy
- UhQx4S7sO1syI8kFxelGag==
- -----END PRIVATE KEY-----
- """;
-
- private static final String ECDSA_VALID_CERTIFICATE = """
- -----BEGIN CERTIFICATE-----
- MIIEZDCCAsygAwIBAgIIe+JXoqra/IUwDQYJKoZIhvcNAQEMBQAwXjERMA8GA1UE
- AxMIc2VydmVyY2ExFDASBgNVBAsTC0RldmVsb3BtZW50MREwDwYDVQQKEwhBa2xp
- dml0eTETMBEGA1UECBMKQ2FsaWZvcm5pYTELMAkGA1UEBhMCVVMwHhcNMjQwMjA1
- MjAxMzMzWhcNMjkwMTA5MjAxMzMzWjAYMRYwFAYDVQQDDA0qLmV4YW1wbGUuY29t
- MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAqE4vDalktxNrVgq8Idae
- 238z4Co9Xu4Do5PW2+gkO6LG2aqEf8HYbxHICEcpMZIy78GGnkQGG6KuoIyBJM+h
- /FMDMWb1meHds967jgE0cJc8OGyE8IpcGNJ13qpKRw12rYTaEKhTKXFzqL3hARjc
- jToxR++0nKpnPK3qEGotqShZn31nNNJHDQmDPsua8ZrFfJLxqzi2GpSVJ00VYe7a
- XWtvflxRLmFgnIhwCtZPhU90KnL1Aeer2b9N+dCdAWja9yq56g1+fErAmrm+lJRd
- z/taxLOSYaxupzSFKPWZRUCEDa4X2bZrfOjAnfHxeI2VxLA9LGuDAwTZeUnh/ddW
- ajo290/OFh1ON1BwbxuD3L8YifOjAP/rynQxwSlyPhlljaF2aGuH6HwCATAmCTiC
- t+XXi/B8I+chAB8+vON1lvPesZlCXBph59LQLrJHwcHCx4aLwdZzLtTosmEyCdMY
- IY/en7vNLbPVbLWCu2gD0+XD6dFNk1P6bR9fkdChH1HXAgMBAAGjbDBqMB0GA1Ud
- DgQWBBS6TIXoFDZ+ik9ZAL/1txt20PvZ6TAOBgNVHQ8BAf8EBAMCBaAwGAYDVR0R
- BBEwD4INKi5leGFtcGxlLmNvbTAfBgNVHSMEGDAWgBSqJw9lbKd9+WdnCKLbDLnS
- FxyoqzANBgkqhkiG9w0BAQwFAAOCAYEAdrU2sI4NMJB4Jb+8k1NTQoq6qh+VkKvq
- hHzc1F13tvXmErl43OjQkfcfCPOSxDeZh5POBycJArXqnbKXl5t7w8YJnWTeuIFm
- XoJ8ZHjMdSuKiU4waPrYVaY5t6q9bOcyXDGjObIiUH1UTn734aKZkVN3+Cwz4Ib0
- fKLjjfpOBXkNpSDps7T/7g0iQTfxwlQUBthVoUkpN5cQV+/xruIErWLdaKcEYN5A
- 4LAnFfUGMtX9Qfr/IbWMuuZuUTzTPjumYn3ccx70rgItCG30dm8RJSyqUx8f43yI
- VnuLCZ3YyV1J5vN56rdOUS+zrMnXJy404eD5aV4g46iyKESg7a8v+ABQ3QkgNnpb
- gUGyiltJyjNMVdFf8UC/CfPBySFB1Hfo7g3/y8FNB7UKLJS0UDm68/mjLUwmasUr
- nfjfcABz8ZwaNGqlb4qWJfA+yI/UCL1FpIBZiaEWmhZsCQwgsFk8vXpI52e3IUxt
- EzpLY+k37xLdk1xkBe59n+hL/hecSiXF
- -----END CERTIFICATE-----
- """;
-
- private static final char[] PASSWORD = "generated".toCharArray();
-
- @Test
- public void shouldVerifyRsaValid() throws Exception
- {
- // GIVEN
- TlsKeyPairVerifier verifier = new TlsKeyPairVerifier();
- KeyStore.PrivateKeyEntry entry = privateKeyEntry(RSA_PRIVATE_KEY, RSA_VALID_CERTIFICATE, PASSWORD);
-
- // WHEN
- boolean valid = verifier.verify(entry);
-
- // THEN
- assertThat(valid, equalTo(true));
- }
-
- @Test
- public void shouldVerifyRsaInvalid() throws Exception
- {
- // GIVEN
- TlsKeyPairVerifier verifier = new TlsKeyPairVerifier();
- KeyStore.PrivateKeyEntry entry = privateKeyEntry(RSA_PRIVATE_KEY, RSA_INVALID_CERTIFICATE, PASSWORD);
-
- // WHEN
- boolean valid = verifier.verify(entry);
-
- // THEN
- assertThat(valid, equalTo(false));
- }
-
- @Test
- public void shouldVerifyEcdsaValid() throws Exception
- {
- // GIVEN
- TlsKeyPairVerifier verifier = new TlsKeyPairVerifier();
- KeyStore.PrivateKeyEntry entry = privateKeyEntry(ECDSA_PRIVATE_KEY, ECDSA_VALID_CERTIFICATE, PASSWORD);
-
- // WHEN
- boolean valid = verifier.verify(entry);
-
- // THEN
- assertThat(valid, equalTo(true));
- }
-
- private static KeyStore.PrivateKeyEntry privateKeyEntry(
- String privateKeyPem,
- String certificatePem,
- char[] password) throws Exception
- {
- PrivateKey privateKey = privateKeyFromPem(privateKeyPem);
- X509Certificate certificate = certificateFromPem(certificatePem);
- KeyStore keyStore = KeyStore.getInstance("PKCS12");
- keyStore.load(null, password);
- X509Certificate[] certChain = {certificate};
- keyStore.setKeyEntry("localhost", privateKey, password, certChain);
- return (KeyStore.PrivateKeyEntry) keyStore.getEntry("localhost", new KeyStore.PasswordProtection(password));
- }
-
- private static PrivateKey privateKeyFromPem(
- String pem) throws Exception
- {
- String privateKeyPem = pem
- .replace("-----BEGIN PRIVATE KEY-----", "")
- .replace("-----END PRIVATE KEY-----", "")
- .replaceAll("\\s", "");
- byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- return keyFactory.generatePrivate(keySpec);
- }
-
- private static X509Certificate certificateFromPem(
- String pem) throws Exception
- {
- String certificatePem = pem
- .replace("-----BEGIN CERTIFICATE-----", "")
- .replace("-----END CERTIFICATE-----", "")
- .replaceAll("\\s", "");
- byte[] decoded = Base64.getDecoder().decode(certificatePem);
- CertificateFactory factory = CertificateFactory.getInstance("X.509");
- return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(decoded));
- }
-}
diff --git a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/streams/ClientIT.java b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/streams/ClientIT.java
index 2a338ec4ab..db4427dd89 100644
--- a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/streams/ClientIT.java
+++ b/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/streams/ClientIT.java
@@ -15,9 +15,9 @@
*/
package io.aklivity.zilla.runtime.binding.tls.internal.streams;
-import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfigurationTest.TLS_CACERTS_STORE_NAME;
-import static io.aklivity.zilla.runtime.binding.tls.internal.TlsConfigurationTest.TLS_CACERTS_STORE_PASS_NAME;
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_DRAIN_ON_CLOSE;
+import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CACERTS_STORE_NAME;
+import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CACERTS_STORE_PASS_NAME;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.rules.RuleChain.outerRule;
@@ -70,8 +70,8 @@ public void shouldEstablishConnection() throws Exception
@Specification({
"${app}/connection.established/client",
"${net}/connection.established/server" })
- @Configure(name = TLS_CACERTS_STORE_NAME, value = "src/test/democa/client/trust")
- @Configure(name = TLS_CACERTS_STORE_PASS_NAME, value = "generated")
+ @Configure(name = ENGINE_CACERTS_STORE_NAME, value = "src/test/democa/client/trust")
+ @Configure(name = ENGINE_CACERTS_STORE_PASS_NAME, value = "generated")
public void shouldEstablishConnectionWithTrustcacerts() throws Exception
{
k3po.finish();
diff --git a/runtime/binding-ws/pom.xml b/runtime/binding-ws/pom.xml
index 245d8a5477..5c13fc5a40 100644
--- a/runtime/binding-ws/pom.xml
+++ b/runtime/binding-ws/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/catalog-apicurio/pom.xml b/runtime/catalog-apicurio/pom.xml
index b813c9848c..114b8ea764 100644
--- a/runtime/catalog-apicurio/pom.xml
+++ b/runtime/catalog-apicurio/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/catalog-apicurio/src/main/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandler.java b/runtime/catalog-apicurio/src/main/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandler.java
index ebbc32c72e..0f435119af 100644
--- a/runtime/catalog-apicurio/src/main/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandler.java
+++ b/runtime/catalog-apicurio/src/main/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandler.java
@@ -38,12 +38,14 @@
import jakarta.json.JsonReader;
import jakarta.json.stream.JsonParsingException;
+import org.agrona.BitUtil;
import org.agrona.DirectBuffer;
import org.agrona.collections.Int2ObjectCache;
import org.agrona.concurrent.UnsafeBuffer;
import io.aklivity.zilla.runtime.catalog.apicurio.config.ApicurioOptionsConfig;
-import io.aklivity.zilla.runtime.catalog.apicurio.internal.types.ApicurioPrefixFW;
+import io.aklivity.zilla.runtime.catalog.apicurio.internal.types.ApicurioDefaultIdFW;
+import io.aklivity.zilla.runtime.catalog.apicurio.internal.types.ApicurioLegacyIdFW;
import io.aklivity.zilla.runtime.engine.EngineContext;
import io.aklivity.zilla.runtime.engine.catalog.CatalogHandler;
import io.aklivity.zilla.runtime.engine.model.function.ValueConsumer;
@@ -60,9 +62,12 @@ public class ApicurioCatalogHandler implements CatalogHandler
private static final long RESET_RETRY_DELAY_MS_DEFAULT = 0L;
private static final long RETRY_INITIAL_DELAY_MS_DEFAULT = 1000L;
- private final ApicurioPrefixFW.Builder prefixRW = new ApicurioPrefixFW.Builder()
+ private final ApicurioLegacyIdFW.Builder legacyIdRW = new ApicurioLegacyIdFW.Builder()
.wrap(new UnsafeBuffer(new byte[5]), 0, 5);
+ private final ApicurioDefaultIdFW.Builder defaultIdRW = new ApicurioDefaultIdFW.Builder()
+ .wrap(new UnsafeBuffer(new byte[9]), 0, 9);
+
private final HttpClient client;
private final String baseUrl;
private final CRC32C crc32c;
@@ -73,13 +78,14 @@ public class ApicurioCatalogHandler implements CatalogHandler
private final long catalogId;
private final String groupId;
private final String useId;
+ private final IdDecoder decodeId;
private final IdEncoder encodeId;
- private final int idSize;
+ private final int sizeofId;
+ private final int encodePadding;
private final String artifactPath;
private final ConcurrentMap> cachedArtifacts;
private final ConcurrentMap> cachedArtifactIds;
-
public ApicurioCatalogHandler(
ApicurioOptionsConfig config,
EngineContext context,
@@ -87,6 +93,7 @@ public ApicurioCatalogHandler(
{
this(config, context, catalogId, new ApicurioCache());
}
+
public ApicurioCatalogHandler(
ApicurioOptionsConfig config,
EngineContext context,
@@ -101,8 +108,10 @@ public ApicurioCatalogHandler(
this.maxAgeMillis = config.maxAge.toMillis();
this.groupId = config.groupId;
this.useId = config.useId;
+ this.decodeId = config.idEncoding.equals(LEGACY_ID_ENCODING) ? this::decodeLegacyId : this::decodeDefaultId;
this.encodeId = config.idEncoding.equals(LEGACY_ID_ENCODING) ? this::encodeLegacyId : this::encodeDefaultId;
- this.idSize = config.idEncoding.equals(LEGACY_ID_ENCODING) ? SIZE_OF_INT : SIZE_OF_LONG;
+ this.sizeofId = config.idEncoding.equals(LEGACY_ID_ENCODING) ? SIZE_OF_INT : SIZE_OF_LONG;
+ this.encodePadding = BitUtil.SIZE_OF_BYTE + sizeofId;
this.artifactPath = useId.equals(CONTENT_ID) ? ARTIFACT_BY_CONTENT_ID_PATH : ARTIFACT_BY_GLOBAL_ID_PATH;
this.event = new ApicurioEventContext(context);
this.catalogId = catalogId;
@@ -325,7 +334,7 @@ public int resolve(
int schemaId = NO_SCHEMA_ID;
if (data.getByte(index) == MAGIC_BYTE)
{
- schemaId = encodeId.encode(data, index + SIZE_OF_BYTE);
+ schemaId = decodeId.decode(data, index + SIZE_OF_BYTE);
}
return schemaId;
}
@@ -346,8 +355,8 @@ public int decode(
if (data.getByte(index) == MAGIC_BYTE)
{
progress += SIZE_OF_BYTE;
- schemaId = encodeId.encode(data, index + progress);
- progress += idSize;
+ schemaId = decodeId.decode(data, index + progress);
+ progress += sizeofId;
}
if (schemaId > NO_SCHEMA_ID)
@@ -368,17 +377,16 @@ public int encode(
ValueConsumer next,
Encoder encoder)
{
- ApicurioPrefixFW prefix = prefixRW.rewrap().schemaId(schemaId).build();
- next.accept(prefix.buffer(), prefix.offset(), prefix.sizeof());
+ int prefixLen = encodeId.encode(schemaId, next);
int valLength = encoder.accept(traceId, bindingId, schemaId, data, index, length, next);
- return valLength > 0 ? prefix.sizeof() + valLength : -1;
+ return valLength > 0 ? prefixLen + valLength : -1;
}
@Override
public int encodePadding(
int length)
{
- return MAX_PADDING_LENGTH;
+ return encodePadding;
}
private URI toURI(
@@ -415,13 +423,29 @@ private int resolveId(
}
private int encodeDefaultId(
+ int schemaId, ValueConsumer next)
+ {
+ ApicurioDefaultIdFW prefix = defaultIdRW.rewrap().schemaId(schemaId).build();
+ next.accept(prefix.buffer(), prefix.offset(), prefix.sizeof());
+ return prefix.sizeof();
+ }
+
+ private int encodeLegacyId(
+ int schemaId, ValueConsumer next)
+ {
+ ApicurioLegacyIdFW prefix = legacyIdRW.rewrap().schemaId(schemaId).build();
+ next.accept(prefix.buffer(), prefix.offset(), prefix.sizeof());
+ return prefix.sizeof();
+ }
+
+ private int decodeDefaultId(
DirectBuffer data,
int index)
{
return (int) data.getLong(index, ByteOrder.BIG_ENDIAN);
}
- private int encodeLegacyId(
+ private int decodeLegacyId(
DirectBuffer data,
int index)
{
@@ -431,6 +455,12 @@ private int encodeLegacyId(
@FunctionalInterface
private interface IdEncoder
{
- int encode(DirectBuffer data, int index);
+ int encode(int schemaId, ValueConsumer next);
+ }
+
+ @FunctionalInterface
+ private interface IdDecoder
+ {
+ int decode(DirectBuffer data, int index);
}
}
diff --git a/runtime/catalog-apicurio/src/main/zilla/internal.idl b/runtime/catalog-apicurio/src/main/zilla/internal.idl
index 6e106c2a6a..b7f3a6b3aa 100644
--- a/runtime/catalog-apicurio/src/main/zilla/internal.idl
+++ b/runtime/catalog-apicurio/src/main/zilla/internal.idl
@@ -16,7 +16,13 @@ scope internal
{
option byteorder network;
- struct ApicurioPrefix
+ struct ApicurioDefaultId
+ {
+ uint8 magic = 0;
+ int64 schemaId;
+ }
+
+ struct ApicurioLegacyId
{
uint8 magic = 0;
int32 schemaId;
diff --git a/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandlerTest.java b/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandlerTest.java
new file mode 100644
index 0000000000..1a50a002fb
--- /dev/null
+++ b/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogHandlerTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc
+ *
+ * Licensed under the Aklivity Community License (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.aklivity.io/aklivity-community-license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package io.aklivity.zilla.runtime.catalog.apicurio.internal;
+
+import static org.agrona.BitUtil.SIZE_OF_BYTE;
+import static org.agrona.BitUtil.SIZE_OF_INT;
+import static org.agrona.BitUtil.SIZE_OF_LONG;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.time.Duration;
+
+import org.agrona.DirectBuffer;
+import org.agrona.MutableDirectBuffer;
+import org.agrona.concurrent.UnsafeBuffer;
+import org.junit.Test;
+
+import io.aklivity.zilla.runtime.catalog.apicurio.config.ApicurioOptionsConfig;
+import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.catalog.CatalogHandler;
+import io.aklivity.zilla.runtime.engine.model.function.ValueConsumer;
+
+public class ApicurioCatalogHandlerTest
+{
+ private static final int SIZE_OF_DEFAULT_PREFIX = SIZE_OF_BYTE + SIZE_OF_LONG;
+ private static final int SIZE_OF_LEGACY_PREFIX = SIZE_OF_BYTE + SIZE_OF_INT;
+
+ private EngineContext context = mock(EngineContext.class);
+
+ @Test
+ public void shouldVerifyDefaultEncodePadding()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ int actual = catalog.encodePadding(0);
+
+ assertEquals(SIZE_OF_DEFAULT_PREFIX, actual);
+ }
+
+ @Test
+ public void shouldVerifyLegacyEncodePadding()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .idEncoding("legacy")
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ int actual = catalog.encodePadding(0);
+
+ assertEquals(SIZE_OF_LEGACY_PREFIX, actual);
+ }
+
+ @Test
+ public void shouldEncodeDefaultSchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ DirectBuffer data = new UnsafeBuffer();
+
+ byte[] bytes = {
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.encode(0L, 0L, 1, data, 0, data.capacity(), ValueConsumer.NOP, CatalogHandler.Encoder.IDENTITY);
+
+ assertEquals(SIZE_OF_DEFAULT_PREFIX + bytes.length, actual);
+ }
+
+ @Test
+ public void shouldEncodeLegacySchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .idEncoding("legacy")
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ DirectBuffer data = new UnsafeBuffer();
+
+ byte[] bytes = {
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.encode(0L, 0L, 1, data, 0, data.capacity(), ValueConsumer.NOP, CatalogHandler.Encoder.IDENTITY);
+
+ assertEquals(SIZE_OF_LEGACY_PREFIX + bytes.length, actual);
+ }
+
+ @Test
+ public void shouldDecodeDefaultSchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ DirectBuffer data = new UnsafeBuffer();
+
+ byte[] bytes = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.decode(0L, 0L, data, 0, data.capacity(), ValueConsumer.NOP, CatalogHandler.Decoder.IDENTITY);
+
+ assertEquals(data.capacity() - SIZE_OF_DEFAULT_PREFIX, actual);
+ }
+
+ @Test
+ public void shouldDecodeLegacySchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .idEncoding("legacy")
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ DirectBuffer data = new UnsafeBuffer();
+
+ byte[] bytes = {
+ 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.decode(0L, 0L, data, 0, data.capacity(), ValueConsumer.NOP, CatalogHandler.Decoder.IDENTITY);
+
+ assertEquals(data.capacity() - SIZE_OF_LEGACY_PREFIX, actual);
+ }
+
+ @Test
+ public void shouldResolveDefaultSchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ MutableDirectBuffer data = new UnsafeBuffer(new byte[SIZE_OF_DEFAULT_PREFIX + 13]);
+
+ data.putByte(0, (byte) 0);
+
+ byte[] bytes = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.resolve(data, 0, data.capacity());
+
+ assertEquals(9, actual);
+ }
+
+ @Test
+ public void shouldResolveLegacySchemaId()
+ {
+ ApicurioOptionsConfig config = ApicurioOptionsConfig.builder()
+ .url("http://localhost:8080")
+ .groupId("groupId")
+ .maxAge(Duration.ofSeconds(1))
+ .idEncoding("legacy")
+ .build();
+
+ ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
+
+ MutableDirectBuffer data = new UnsafeBuffer(new byte[SIZE_OF_LEGACY_PREFIX + 13]);
+
+ data.putByte(0, (byte) 0);
+
+ byte[] bytes = {
+ 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x06, 0x69, 0x64, 0x30, 0x10, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65 };
+
+ data.wrap(bytes, 0, bytes.length);
+
+ int actual = catalog.resolve(data, 0, data.capacity());
+
+ assertEquals(9, actual);
+ }
+}
diff --git a/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogIT.java b/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogIT.java
index ca55dc20c0..010561c152 100644
--- a/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogIT.java
+++ b/runtime/catalog-apicurio/src/test/java/io/aklivity/zilla/runtime/catalog/apicurio/internal/ApicurioCatalogIT.java
@@ -15,15 +15,8 @@
package io.aklivity.zilla.runtime.catalog.apicurio.internal;
import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.junit.Assert.assertEquals;
import static org.junit.rules.RuleChain.outerRule;
-import static org.mockito.Mockito.mock;
-import java.time.Duration;
-
-import org.agrona.DirectBuffer;
-import org.agrona.concurrent.UnsafeBuffer;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.DisableOnDebug;
@@ -32,10 +25,6 @@
import io.aklivity.k3po.runtime.junit.annotation.Specification;
import io.aklivity.k3po.runtime.junit.rules.K3poRule;
-import io.aklivity.zilla.runtime.catalog.apicurio.config.ApicurioOptionsConfig;
-import io.aklivity.zilla.runtime.engine.EngineContext;
-import io.aklivity.zilla.runtime.engine.catalog.CatalogHandler;
-import io.aklivity.zilla.runtime.engine.model.function.ValueConsumer;
import io.aklivity.zilla.runtime.engine.test.EngineRule;
import io.aklivity.zilla.runtime.engine.test.annotation.Configuration;
@@ -58,19 +47,6 @@ public class ApicurioCatalogIT
@Rule
public final TestRule chain = outerRule(engine).around(k3po).around(timeout);
- private ApicurioOptionsConfig config;
- private EngineContext context = mock(EngineContext.class);
-
- @Before
- public void setup()
- {
- config = ApicurioOptionsConfig.builder()
- .url("http://localhost:8080")
- .groupId("groupId")
- .maxAge(Duration.ofSeconds(1))
- .build();
- }
-
@Test
@Configuration("resolve/artifact/global/id/zilla.yaml")
@Specification({
@@ -158,60 +134,4 @@ public void shouldResolveArtifactIdViaSubjectAndVersionFailed() throws Exception
{
k3po.finish();
}
-
- @Test
- public void shouldVerifyMaxPadding()
- {
- ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
-
- assertEquals(9, catalog.encodePadding(0));
- }
-
- @Test
- public void shouldVerifyEncodedData()
- {
- ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
-
- DirectBuffer data = new UnsafeBuffer();
-
- byte[] bytes = {0x06, 0x69, 0x64,
- 0x30, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65};
- data.wrap(bytes, 0, bytes.length);
-
- assertEquals(18, catalog.encode(0L, 0L, 1, data, 0, data.capacity(),
- ValueConsumer.NOP, CatalogHandler.Encoder.IDENTITY));
- }
-
- @Test
- public void shouldResolveSchemaIdAndProcessData()
- {
-
- ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
-
- DirectBuffer data = new UnsafeBuffer();
-
- byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x69, 0x64,
- 0x30, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65};
- data.wrap(bytes, 0, bytes.length);
-
- int valLength = catalog.decode(0L, 0L, data, 0, data.capacity(), ValueConsumer.NOP, CatalogHandler.Decoder.IDENTITY);
-
- assertEquals(data.capacity() - 9, valLength);
- }
-
- @Test
- public void shouldResolveSchemaIdFromData()
- {
- ApicurioCatalogHandler catalog = new ApicurioCatalogHandler(config, context, 0L);
-
- DirectBuffer data = new UnsafeBuffer();
-
- byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x69, 0x64,
- 0x30, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65};
- data.wrap(bytes, 0, bytes.length);
-
- int schemaId = catalog.resolve(data, 0, data.capacity());
-
- assertEquals(9, schemaId);
- }
}
diff --git a/runtime/catalog-filesystem/pom.xml b/runtime/catalog-filesystem/pom.xml
index 0ca6dfc101..3ce3da669f 100644
--- a/runtime/catalog-filesystem/pom.xml
+++ b/runtime/catalog-filesystem/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/catalog-inline/pom.xml b/runtime/catalog-inline/pom.xml
index af975878f0..ce34f6c244 100644
--- a/runtime/catalog-inline/pom.xml
+++ b/runtime/catalog-inline/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/catalog-karapace/pom.xml b/runtime/catalog-karapace/pom.xml
index fdfa165235..c4b1b876ea 100644
--- a/runtime/catalog-karapace/pom.xml
+++ b/runtime/catalog-karapace/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/catalog-schema-registry/pom.xml b/runtime/catalog-schema-registry/pom.xml
index a994e4bc10..435ecf4a7c 100644
--- a/runtime/catalog-schema-registry/pom.xml
+++ b/runtime/catalog-schema-registry/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/command-metrics/pom.xml b/runtime/command-metrics/pom.xml
index 21a790cbc3..2f4ffd2212 100644
--- a/runtime/command-metrics/pom.xml
+++ b/runtime/command-metrics/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/command-start/pom.xml b/runtime/command-start/pom.xml
index f32caf7000..060363c0fc 100644
--- a/runtime/command-start/pom.xml
+++ b/runtime/command-start/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/command-stop/pom.xml b/runtime/command-stop/pom.xml
index 9236106538..02e5d640f0 100644
--- a/runtime/command-stop/pom.xml
+++ b/runtime/command-stop/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/command-version/pom.xml b/runtime/command-version/pom.xml
index cab2437c2f..559d0e69bd 100644
--- a/runtime/command-version/pom.xml
+++ b/runtime/command-version/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/command/pom.xml b/runtime/command/pom.xml
index f02e70ef1a..b38f662fac 100644
--- a/runtime/command/pom.xml
+++ b/runtime/command/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/common/pom.xml b/runtime/common/pom.xml
index 122a8d193a..794dc1f806 100644
--- a/runtime/common/pom.xml
+++ b/runtime/common/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/engine/pom.xml b/runtime/engine/pom.xml
index b50c84b478..264c0bc6a1 100644
--- a/runtime/engine/pom.xml
+++ b/runtime/engine/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java
index c843495e29..7fcaeb3a22 100644
--- a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java
+++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java
@@ -30,6 +30,7 @@
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.security.KeyStore;
import java.util.Properties;
import java.util.function.Function;
@@ -76,6 +77,9 @@ public class EngineConfiguration extends Configuration
public static final BooleanPropertyDef ENGINE_VERBOSE_SCHEMA_PLAIN;
public static final BooleanPropertyDef ENGINE_VERBOSE_COMPOSITES;
public static final IntPropertyDef ENGINE_WORKERS;
+ public static final PropertyDef ENGINE_CACERTS_STORE_TYPE;
+ public static final PropertyDef ENGINE_CACERTS_STORE;
+ public static final PropertyDef ENGINE_CACERTS_STORE_PASS;
private static final ConfigurationDef ENGINE_CONFIG;
@@ -120,6 +124,9 @@ public class EngineConfiguration extends Configuration
ENGINE_VERBOSE_SCHEMA = config.property("verbose.schema", false);
ENGINE_VERBOSE_SCHEMA_PLAIN = config.property("verbose.schema.plain", false);
ENGINE_WORKERS = config.property("workers", Runtime.getRuntime().availableProcessors());
+ ENGINE_CACERTS_STORE_TYPE = config.property("cacerts.store.type", EngineConfiguration::cacertsStoreTypeDefault);
+ ENGINE_CACERTS_STORE = config.property("cacerts.store", EngineConfiguration::cacertsStoreDefault);
+ ENGINE_CACERTS_STORE_PASS = config.property("cacerts.store.pass");
ENGINE_CONFIG = config;
}
@@ -299,6 +306,21 @@ public int workers()
return ENGINE_WORKERS.getAsInt(this);
}
+ public String cacertsStoreType()
+ {
+ return ENGINE_CACERTS_STORE_TYPE.get(this);
+ }
+
+ public String cacertsStore()
+ {
+ return ENGINE_CACERTS_STORE.get(this);
+ }
+
+ public String cacertsStorePass()
+ {
+ return ENGINE_CACERTS_STORE_PASS.get(this);
+ }
+
public Function hostResolver()
{
return ENGINE_HOST_RESOLVER.get(this)::resolve;
@@ -448,4 +470,16 @@ private static URI defaultConfigURI(
URL url = ENGINE_CONFIG_URL.get(config);
return decodeConfigURI(config, url.toString());
}
+
+ private static String cacertsStoreTypeDefault(
+ Configuration config)
+ {
+ return System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
+ }
+
+ private static String cacertsStoreDefault(
+ Configuration config)
+ {
+ return System.getProperty("javax.net.ssl.trustStore");
+ }
}
diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineWorker.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineWorker.java
index 77df554429..41182d331b 100644
--- a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineWorker.java
+++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineWorker.java
@@ -393,7 +393,15 @@ public EngineWorker(
for (Vault vault : vaults)
{
String type = vault.name();
- vaultsByType.put(type, vault.supply(this));
+ Set aliases = vault.aliases();
+
+ VaultContext context = vault.supply(this);
+
+ vaultsByType.put(type, context);
+ for (String alias : aliases)
+ {
+ vaultsByType.put(alias, context);
+ }
}
Map catalogsByType = new LinkedHashMap<>();
diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/Trusted.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/Trusted.java
new file mode 100644
index 0000000000..f92ffd435c
--- /dev/null
+++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/Trusted.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.engine.security;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+import io.aklivity.zilla.runtime.engine.Configuration;
+import io.aklivity.zilla.runtime.engine.EngineConfiguration;
+
+public final class Trusted
+{
+ private Trusted()
+ {
+ }
+
+ public static KeyStore cacerts(
+ Configuration config)
+ {
+ return config instanceof EngineConfiguration engineConfig
+ ? cacerts(engineConfig)
+ : cacerts(new EngineConfiguration(config));
+ }
+
+ private static KeyStore cacerts(
+ EngineConfiguration config)
+ {
+ String storeType = config.cacertsStoreType();
+ String storePath = config.cacertsStore();
+ String storePass = config.cacertsStorePass();
+
+ KeyStore cacerts = null;
+
+ if (storePath == null || !Files.exists(Paths.get(storePath)))
+ {
+ String home = System.getProperty("java.home");
+
+ storePath = String.format("%s/lib/security/jssecacerts", home);
+
+ if (!Files.exists(Paths.get(storePath)))
+ {
+ storePath = String.format("%s/lib/security/cacerts", home);
+
+ if (!Files.exists(Paths.get(storePath)))
+ {
+ storePath = null;
+ }
+ }
+ }
+
+ if (storePath != null)
+ {
+ try
+ {
+ KeyStore store = KeyStore.getInstance(storeType);
+ store.load(new FileInputStream(storePath), storePass != null ? storePass.toCharArray() : null);
+
+ cacerts = store;
+ }
+ catch (GeneralSecurityException | IOException ex)
+ {
+ // unable to load
+ }
+ }
+
+ return cacerts;
+ }
+}
diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/Vault.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/Vault.java
index 4a5fa29425..7eb2d8520d 100644
--- a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/Vault.java
+++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/Vault.java
@@ -18,8 +18,9 @@
import java.net.URL;
import io.aklivity.zilla.runtime.engine.EngineContext;
+import io.aklivity.zilla.runtime.engine.factory.Aliasable;
-public interface Vault
+public interface Vault extends Aliasable
{
String name();
diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/VaultHandler.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/VaultHandler.java
index 6c67d6c856..90b2c68d69 100644
--- a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/VaultHandler.java
+++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/vault/VaultHandler.java
@@ -16,15 +16,20 @@
package io.aklivity.zilla.runtime.engine.vault;
import java.security.KeyStore;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
public interface VaultHandler
{
- KeyStore.PrivateKeyEntry key(
- String ref);
+ KeyManagerFactory initKeys(
+ List keyRefs);
- KeyStore.TrustedCertificateEntry certificate(
- String ref);
+ KeyManagerFactory initSigners(
+ List signerRefs);
- KeyStore.PrivateKeyEntry[] keys(
- String signerRef);
+ TrustManagerFactory initTrust(
+ List certRefs,
+ KeyStore cacerts);
}
diff --git a/runtime/engine/src/main/moditect/module-info.java b/runtime/engine/src/main/moditect/module-info.java
index 92a07f0854..1526e8d0e2 100644
--- a/runtime/engine/src/main/moditect/module-info.java
+++ b/runtime/engine/src/main/moditect/module-info.java
@@ -32,6 +32,7 @@
exports io.aklivity.zilla.runtime.engine.metrics.reader;
exports io.aklivity.zilla.runtime.engine.reader;
exports io.aklivity.zilla.runtime.engine.resolver;
+ exports io.aklivity.zilla.runtime.engine.security;
exports io.aklivity.zilla.runtime.engine.util.function;
exports io.aklivity.zilla.runtime.engine.vault;
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/EngineConfigurationTest.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/EngineConfigurationTest.java
index c32db89391..7e6270907d 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/EngineConfigurationTest.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/EngineConfigurationTest.java
@@ -17,9 +17,15 @@
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_BUFFER_POOL_CAPACITY;
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_BUFFER_SLOT_CAPACITY;
+import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_CACERTS_STORE;
+import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_CACERTS_STORE_PASS;
+import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_CACERTS_STORE_TYPE;
import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_CONFIG_URL;
import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_BUFFER_POOL_CAPACITY_NAME;
import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_BUFFER_SLOT_CAPACITY_NAME;
+import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CACERTS_STORE_NAME;
+import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CACERTS_STORE_PASS_NAME;
+import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CACERTS_STORE_TYPE_NAME;
import static io.aklivity.zilla.runtime.engine.test.EngineRule.ENGINE_CONFIG_URL_NAME;
import static org.junit.Assert.assertEquals;
@@ -33,5 +39,8 @@ public void shouldVerifyConstants() throws Exception
assertEquals(ENGINE_BUFFER_POOL_CAPACITY.name(), ENGINE_BUFFER_POOL_CAPACITY_NAME);
assertEquals(ENGINE_BUFFER_SLOT_CAPACITY.name(), ENGINE_BUFFER_SLOT_CAPACITY_NAME);
assertEquals(ENGINE_CONFIG_URL.name(), ENGINE_CONFIG_URL_NAME);
+ assertEquals(ENGINE_CACERTS_STORE_TYPE.name(), ENGINE_CACERTS_STORE_TYPE_NAME);
+ assertEquals(ENGINE_CACERTS_STORE.name(), ENGINE_CACERTS_STORE_NAME);
+ assertEquals(ENGINE_CACERTS_STORE_PASS.name(), ENGINE_CACERTS_STORE_PASS_NAME);
}
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/config/NamespaceConfigAdapterTest.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/config/NamespaceConfigAdapterTest.java
index bb9d04151e..2131e07aaa 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/config/NamespaceConfigAdapterTest.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/internal/config/NamespaceConfigAdapterTest.java
@@ -49,6 +49,147 @@
public class NamespaceConfigAdapterTest
{
+ private static final String LOCALHOST_KEY = """
+ -----BEGIN PRIVATE KEY-----
+ MIIJRQIBADANBgkqhkiG9w0BAQEFAASCCS8wggkrAgEAAoICAQDPk6zIt8JoQLQQ
+ AIYYws3lJAq6tBjPFTuuqgvly/EelozPzW4ycbpXXrpGSe3EU7tBBcGuv81gO3UX
+ VlYGQQroY1kXB1khZM8YTBcCZMq0ByunmKRF6nGxhHPDu7qmQhreRDioodsxiN0I
+ PxCjgxBKTFniF3hFVG8btPB6WMr39oyHGon0RlMqV92ia8c+XFnTHWyqVc10KhSA
+ WNpeF7Boic1QF7oi5EWVz2bJbI1tOSeUz8C1OlUhb9r8/zFvacJSIdCwhOtuHHJu
+ 1pSdIiOYzMtJfXWkueN2hc46tZQ3ZoyD27u8GMkk1dA1tPH9ouZlS13UoxcinOuE
+ ffMiONrT2miAmeqGQwbyNENNcdyMec4+fWJyft01+zNf5w5ybZgpYy5N8H5tVjwi
+ cOq9Mx+atBxVimaRmj9wh0PofqLgpaxrXHk8ToiMAkoveIsdjFq/JXrV2RvVPaU4
+ YeivwZiA9DTZ1USRPU3YA/zYTf+Dm/cBxEK9wMfygQoP8zaO3RWNFe4Ulpw883r5
+ O4fyhxvVVIngUcoVXi2wfBanW3ErnvYZVJ9RuZHiFnB0jzBhMuj0yedgYnuopMzY
+ EjIfXtf5ep5k0pMPLQYCQ9Wp9ge9txbWy2V0wZJUFBX7yMFs1/gFG/hxxD4w40Uu
+ xcIDG+26LrY9HfOA7tEB+GA/LRZjkQIDAQABAoICAQDG+KMS8zHihMMU46umaHS8
+ REQUmzV8qrm+vzkQWOETlPP87MnIiMM5pI+heJP1MN25gi8ZtrjCmbuvVw62h/pQ
+ r4piTKTfIaZxf94+aSb0UjtCN0qfyg6ZPoFJCdXsMElY7MPywNM/NBXLJchpM+SV
+ k4JE+oJK0ph+Un6AiERmU2p4xrOd9xsY54iHfBBMcnGXsAjNbdm2k+9657DJqKNs
+ UUsAjv3ZHD6nT1sTkH4wSCzstAfgr72Sg4nCIUvdo96ko81Kpt6VeacnH4Ds7iB5
+ AzWJiD0QXS7wGWqJVCxyvlXKvwHBV3DXYixmjr+3hEKcrhWPEZXHk+sd4S0BjMYT
+ up7mj2GmVSTwL/XS7cCVk4QS4901P2xhnINrw40W40JWinFJ3mW2Z4oySTNsWy0N
+ hugHlw+O0fRFPlVwh9uu8fpLIJQAbO0XwnJ2y7G1dkvFzTEtAxHyVanfwvepwAHl
+ +pszn/LsvAcLO9ncWUvt2pGm4Zz3sAHDYHoTEUUFvJfF33m49Vdgl9SeHVcAIbXc
+ XOnaw8NLoxyR4Q+jtvlr1yFF8Kt3zrA02Bj9W2TIyWJdbwJLjfTK7X4l9Bzin1Ec
+ 17r4XCEsWSKQ9E19AmSriNoGLczhfDCr26BQDVLCwmnKmF86CWrL53PZ9iEXEEVA
+ yruQItpmIRsG4Fu5FKkNAQKCAQEA7Iom5vVMt0sibUYozumEQJ/FdG1vyIn5a3tj
+ ROqGExFy9fwAQi734a45VuooUw7ot9kaGaGrqy6l2a0EyxJNUnF90cuvfX/ZrFwk
+ /w9fWc0TSOFlEVCraSghmZ9TsGAg9NyhGQa1HNrq4XnR9LSWftyOWr6GGUJeufdn
+ qs/gpBBDtMVIAbusbtxuEAMtIfbuc3vvhP0HdTmeXAnUcgQCC+kPZTh/iE0YhCdZ
+ WKxRWoj5HhSMrWvmL/29G3/qN8eMzz0aZLjCweCi8lji69QIVyf90y0MAQ0e20EC
+ n+HX7gzdAvpS+K1I+lMYh9PivcYjiO3fVPMoHUCd6EiKiEy1GQKCAQEA4KeG3/Z9
+ PzSgNWq0HZi62CM/ixFNxgxsPKFxK4zntOAIQIihPR8CpT3Z5UeD4SbP7f5Jep49
+ 7y7zc5CaIb0+bNVSdoSIp/d6/0iNgXwhJEkP3JIpWbhlT/K1OF8V2ItvOFfBkMYl
+ WmHH+V1i00CQAthczmrdYMWCr38S+Pfbcz1ywNyLH1Unh4xo1USXADgpISfLapDH
+ AlUexIyJE3X8XurR0h7BIiAc8oyRfPVEtyASz/aDxLVrG9HpSWS8fsMcQWUKHGBa
+ KrK0sFi7Tc9OPCnLU6dnCT6cLM0qqGGLqqM8Cmmcz+HdhHAAt5XBn53HhERvM1og
+ VtlNosVEl5u5OQKCAQEAnx0roA8ARQgop2Mbjlws15/iHjiDil2txyxgEXrFJ8yE
+ DY4vylV373rYHWw0JfMQfqNu2DEVnngpnmyxnby5AK1RWq/uY7h9/2CYjm6T0H+P
+ 6mWcK/Kc64bQW1t+21U+thg30fLeIAPvHi9pGXflCH9qzwX8hL9No0EWniNp1FMQ
+ iGhw0KGjE4v6CZFpacCGlG6ZJ1diDevtZ7JBE1U43zQuZAOGXnSl+jfR9UEtFH6x
+ PRfLrdi4Ji5EaFw6fL0iLkHHIFvcvrhSRD2gn8yos6A2MTjLK2XdDNYFYvFz5DEI
+ 9rjW2WsPfTwqcywICWpgevqwUZ+jq1HNJvStI5Sd+QKCAQEAnAHaeNcMXQMnqlCi
+ KddxET1RGDr4/mqME4KtO2gLVCErudzn07EgPi34je8e1xED3irzTfJr4hiBuaQW
+ VQ29Nwjgzir1V2dWA8eXdO8FeNQ/7pWVn5ecy2spi68EVa9mmgLfCbGAKQa0Pygp
+ w7gXCdLEiBfQCi6+tS6St1AwFhP7B5FgD28sF0ZbWpbaIa3eagbfjO5jNOx1hFpv
+ qpMJocSB1t/CkPcqAwm40sTkZiMgzUhMcyLk8ZnQ3kXVXFYT3hnTbqc+ll4pejj0
+ QXGPy9neOAaNV+8htz72u52ZxvK6dCSpX/dixGCfLt4Ras2/ystXSZrx0D3xWvKQ
+ 0pOyiQKCAQEAtGU0KDx5aC+zpf1Jd6xd9jPoKY9OBLIuwp1BL7Oio1d7JKrxleOt
+ zjtZrxZXFDdFDUeOCPy4xWv/zblbVIRhp93ruQX/j5bDZvWo85aozTf80t2+aZgr
+ 5msAz3/UBQ5GY0ASJreJlbuSfPn5s9fLBbHNgFg29cmqjxPKU8vUiHXNxcBbRhpU
+ Mp/Kplij+O6hKJeqECq5610CgqLg24vPLQKDsXtC2+ZAHvDJzShN8nu1FQnsqmsj
+ xuxpFoXU+KgeFwOIHD2SkB7w2kmvHQ2TUnvqbcWxcA6F2o0U9uEAAWYE7iq6V1hD
+ HiTCqXKPrP1QCU1/TXWVjEgnVnlruNTahA==
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIFYDCCA0gCCQDYZ1VzcCw3pjANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJV
+ UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREwDwYD
+ VQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMMB1Rl
+ c3QgQ0EwHhcNMjExMjIxMjMwNDE0WhcNMzExMjE5MjMwNDE0WjBzMQswCQYDVQQG
+ EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREw
+ DwYDVQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNVBAMM
+ CWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM+TrMi3
+ wmhAtBAAhhjCzeUkCrq0GM8VO66qC+XL8R6WjM/NbjJxuldeukZJ7cRTu0EFwa6/
+ zWA7dRdWVgZBCuhjWRcHWSFkzxhMFwJkyrQHK6eYpEXqcbGEc8O7uqZCGt5EOKih
+ 2zGI3Qg/EKODEEpMWeIXeEVUbxu08HpYyvf2jIcaifRGUypX3aJrxz5cWdMdbKpV
+ zXQqFIBY2l4XsGiJzVAXuiLkRZXPZslsjW05J5TPwLU6VSFv2vz/MW9pwlIh0LCE
+ 624ccm7WlJ0iI5jMy0l9daS543aFzjq1lDdmjIPbu7wYySTV0DW08f2i5mVLXdSj
+ FyKc64R98yI42tPaaICZ6oZDBvI0Q01x3Ix5zj59YnJ+3TX7M1/nDnJtmCljLk3w
+ fm1WPCJw6r0zH5q0HFWKZpGaP3CHQ+h+ouClrGtceTxOiIwCSi94ix2MWr8letXZ
+ G9U9pThh6K/BmID0NNnVRJE9TdgD/NhN/4Ob9wHEQr3Ax/KBCg/zNo7dFY0V7hSW
+ nDzzevk7h/KHG9VUieBRyhVeLbB8FqdbcSue9hlUn1G5keIWcHSPMGEy6PTJ52Bi
+ e6ikzNgSMh9e1/l6nmTSkw8tBgJD1an2B723FtbLZXTBklQUFfvIwWzX+AUb+HHE
+ PjDjRS7FwgMb7boutj0d84Du0QH4YD8tFmORAgMBAAEwDQYJKoZIhvcNAQELBQAD
+ ggIBAAjyCVGqLUl1EGpRmAAcwtFi2uy7isW+RoyQFOycY5hQBi83KxQ9jnl2VmfO
+ A3kb1AKlPhCyNMlaW+qTxiwdWtEx3lf6Efm83ePsbwialMGb0ybQRRdvyEOkw5LO
+ Q5TOUI7R5tijZQMb6qxPjOJwkgQRl6iOqIDAZmO1ttnqZgxtWCWpajLtCpWO2nDk
+ fLq5UsEFv5heyheUjtOu9pGRzNNAHFMgtOqsAmH8wOTqjxAf3YtMPSanM+fW738T
+ akd1mFhtSp2YjVDMUggix9IrFcBJTpDZBHQJdeBPVjoslfGtVaTcpFBSzcqboCwL
+ 8eJwoFYqBzekV0ZjSY2Vo0z9d6TkNDptrwZYDk9MgmN1qV3coBBCTRYxRUhA/kqF
+ slO3nb+RlcUVwQCZY9twzO845kRsrwaT/xpcmuMCA7xSvKAPVz8nDOEAaZF4CISv
+ mRa2Td6UWajJ/RB0G4BkTO+fBa68sWyIFOANAenRP2laMCoLqAS2ApORHVaZ3d3x
+ bF7Mf+BG70ukLzGwK/6XPe79xEr8F3X9eBJ0sbTqXrgvNmpKN/qIixdDqa6UQDUr
+ 7g2E6OCYhMgxXmoWAMshYRTBEVlsG6EGn0v6m5IzWAua+Kg5Jur8j8+JEUArsvt8
+ MdoPFL6oo+FNgQrkHwHkiONYd+iuunJTJEeXFQEzpoxNvrc1
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIFXjCCA0YCCQCuorYrG5wG+DANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJV
+ UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREwDwYD
+ VQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMMB1Rl
+ c3QgQ0EwHhcNMjExMjIxMjMwNDExWhcNMzExMjE5MjMwNDExWjBxMQswCQYDVQQG
+ EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRvMREw
+ DwYDVQQKDAhBa2xpdml0eTEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEDAOBgNVBAMM
+ B1Rlc3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGPVgVO/zd
+ ebwGWujKymJmztWZ5LIaZC+zY1SwKUBUA3+vrtO79ndi6WePiV0a2e7wov/ajFLp
+ mor2RfGSMD8Yb9e98QSqnfy9Q5+ABmxFulgSJNwDjnxugZuk/6MILKMg7AsgqaxK
+ wROSSLcom8b+gkbwXgHm57RKiitXlRM9ujdKibeHwfu7JTk6A7LwRbCVurTRqckw
+ Q0/mA4mNuZ2AMGW+YL36TwTLfTAa4AVHEbI3U5+TyY3DoV7OoHI4Ec1/7B0CGzqK
+ smKM3dKmXpRIc5NBZt+eKqphAhp0CD1eAnutWtepahjWyY1fAYk9hZ+ayU52dAMf
+ +TbkPdMn5jfHhqs95VdfQjsKZPyNTYjhjHN9tAph1wKUG4XRATAvxhA2gpYgN9de
+ 9ztWPboVzGosauQxPrXklO8CF7hsft0RlCCP9ojVLUkZ42vI/M1S3lD8pCDtPe46
+ 2zQ9S3F1R7goF3AqWm4EQqu237+zL45pCbbWyyHeXHeDrv3DNXHcWXoFicNmCBl6
+ nPPsVn9qgdhmJf5QcUKLkJEEtk94Uedv5qEqiJQYSPAIZHKnv4L5Li69kghTbUv/
+ Xquz2JdY5daj5eRurgZVjutkmMIaR4rJdhifBonlcKxoeSZoVbnoGzS5KcF9saz8
+ 9qYU9LtF98CUMY7U4RPlVbA8D4YwDICgcwIDAQABMA0GCSqGSIb3DQEBCwUAA4IC
+ AQDEzoEbCsHpae5M1I+vezg7w5NAItMU4WU9IccTylSB/gfIT+hWwIv9KiqTWjxw
+ Y5Aj6XJ1mATHAMSQnNZCnP2Hw39/Nc3HcKmek2na2zK/TBSEFXudJmox8SK32r26
+ nLstNlcYf7ixqJ5T7SOE2GJOcEUWpvTSbvQD0NvG81BVnSyUfX3FgkQLwwlyBoSE
+ 7FwFz+ybrbisUHHqzPVnSblEDbKv6T9ai3FjbBegzPVSd9RmtB/DzxhdSk+kL1oD
+ VSEPweSHEqamEnq2RIgLb7rYhmfohl0fGF5W6I3LvLqqe0KLRRID9V/jwBUGyICG
+ W3jGu+68jOIUqXA4+gfOwXNktd4F7So48ySbghgrY0Umr4KSs6CTHhvSZ4ZG8QO/
+ ZyC+DjXsU3mihIBP/Q43YU7dYxFSdlCw79YnXvdWu7K7lZ1bIcbdH+RShcbvPcwg
+ iM2qAvCgZBA8xHMDQeev8QdQjxtN+uBfee0mkvbzPbIh/0prywPHjAie/bXVBPVt
+ VK6Gej2egPCIA5ThvGpmXh8kPd5Aqy1J++cmrzfYfPPsbmPGTLI0HFMhUuzIhFbd
+ TzAV/Qj83r722s6f0v3KEEhfi3EZu3bRSGIyxVtebtOLGvEb2PjJrktyVJgivVFX
+ uHHpz76QFOcLy1F962Hfj51NnIROOySyl12JkhPRTlMoiQ==
+ -----END CERTIFICATE-----
+ """;
+
+ private static final String SERVERCA_TRUST = """
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ """;
+
@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
@@ -240,7 +381,8 @@ public void shouldWriteNamespaceWithVault()
.name("default")
.type("test")
.options(TestVaultOptionsConfig::builder)
- .mode("test")
+ .key("localhost", LOCALHOST_KEY)
+ .trust("serverca", SERVERCA_TRUST)
.build()
.build()
.build();
@@ -249,7 +391,10 @@ public void shouldWriteNamespaceWithVault()
assertThat(text, not(nullValue()));
assertThat(text, equalTo("{\"name\":\"test\",\"vaults\":{\"default\":{\"type\":\"test\"," +
- "\"options\":{\"mode\":\"test\"}}}}"));
+ "\"options\":{" +
+ "\"key\":{\"alias\":\"localhost\",\"entry\":\"%s\"},".formatted(LOCALHOST_KEY.replaceAll("\n", "\\\\n")) +
+ "\"trust\":{\"alias\":\"serverca\",\"entry\":\"%s\"}".formatted(SERVERCA_TRUST.replaceAll("\n", "\\\\n")) +
+ "}}}}"));
}
@Test
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/metrics/reader/MetricsReaderTest.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/metrics/reader/MetricsReaderTest.java
index e8dbc87e04..dab43a6720 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/metrics/reader/MetricsReaderTest.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/metrics/reader/MetricsReaderTest.java
@@ -15,8 +15,8 @@
*/
package io.aklivity.zilla.runtime.engine.metrics.reader;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -46,6 +46,7 @@ public class MetricsReaderTest
};
@Test
+ @SuppressWarnings("unchecked")
public void shouldCollectMetrics()
{
// GIVEN
@@ -78,6 +79,7 @@ public void shouldCollectMetrics()
}
@Test
+ @SuppressWarnings("unchecked")
public void shouldReturnEmptyList()
{
// GIVEN
diff --git a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrustTest.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/security/TrustedTest.java
similarity index 56%
rename from runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrustTest.java
rename to runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/security/TrustedTest.java
index 63df3825ec..36cba89808 100644
--- a/runtime/binding-tls/src/test/java/io/aklivity/zilla/runtime/binding/tls/internal/config/TlsTrustTest.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/security/TrustedTest.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-package io.aklivity.zilla.runtime.binding.tls.internal.config;
+package io.aklivity.zilla.runtime.engine.security;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.emptyArray;
@@ -21,25 +21,50 @@
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
+import java.security.KeyStore;
import java.security.KeyStore.TrustedCertificateEntry;
import java.security.cert.X509Certificate;
+import java.util.Collections;
import org.junit.Test;
-import io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration;
import io.aklivity.zilla.runtime.engine.Configuration;
+import io.aklivity.zilla.runtime.engine.EngineConfiguration;
-public class TlsTrustTest
+public class TrustedTest
{
@Test
- public void shouldConfigureTrustViaCacerts()
+ public void shouldConfigureTrustViaCacerts() throws Exception
{
- TlsConfiguration config = new TlsConfiguration(new Configuration());
- TrustedCertificateEntry[] entries = TlsTrust.cacerts(config);
+ EngineConfiguration config = new EngineConfiguration(new Configuration());
+ KeyStore cacerts = Trusted.cacerts(config);
+
+ assertThat(cacerts, not(nullValue()));
+
+ TrustedCertificateEntry[] entries = Collections.list(cacerts.aliases()).stream()
+ .map(a -> lookup(cacerts, a))
+ .toArray(TrustedCertificateEntry[]::new);
- assertThat(entries, not(nullValue()));
assertThat(entries, not(emptyArray()));
assertThat(entries[0], not(nullValue()));
assertThat(entries[0].getTrustedCertificate(), instanceOf(X509Certificate.class));
}
+
+ private static TrustedCertificateEntry lookup(
+ KeyStore store,
+ String alias)
+ {
+ TrustedCertificateEntry cert = null;
+
+ try
+ {
+ cert = (TrustedCertificateEntry) store.getEntry(alias, null);
+ }
+ catch (Exception ex)
+ {
+ // ignore
+ }
+
+ return cert;
+ }
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/EngineRule.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/EngineRule.java
index 28d3f803f0..93c4267ae4 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/EngineRule.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/EngineRule.java
@@ -66,6 +66,9 @@ public final class EngineRule implements TestRule
public static final String ENGINE_BUFFER_POOL_CAPACITY_NAME = "zilla.engine.buffer.pool.capacity";
public static final String ENGINE_BUFFER_SLOT_CAPACITY_NAME = "zilla.engine.buffer.slot.capacity";
public static final String ENGINE_CONFIG_URL_NAME = "zilla.engine.config.url";
+ public static final String ENGINE_CACERTS_STORE_TYPE_NAME = "zilla.engine.cacerts.store.type";
+ public static final String ENGINE_CACERTS_STORE_NAME = "zilla.engine.cacerts.store";
+ public static final String ENGINE_CACERTS_STORE_PASS_NAME = "zilla.engine.cacerts.store.pass";
private static final long EXTERNAL_AFFINITY_MASK = 1L << (Long.SIZE - 1);
private static final Pattern DATA_FILENAME_PATTERN = Pattern.compile("data\\d+");
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/DuplexIT.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/DuplexIT.java
index 7ead77756d..177e49b0de 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/DuplexIT.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/DuplexIT.java
@@ -15,25 +15,15 @@
*/
package io.aklivity.zilla.runtime.engine.test.internal;
-import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasProperty;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.isA;
import static org.junit.rules.RuleChain.outerRule;
-import org.junit.ComparisonFailure;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.DisableOnDebug;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
-import org.junit.runners.model.TestTimedOutException;
import io.aklivity.k3po.runtime.junit.annotation.Specification;
import io.aklivity.k3po.runtime.junit.rules.K3poRule;
@@ -45,10 +35,8 @@ public class DuplexIT
private final TestRule timeout = new DisableOnDebug(new Timeout(5, SECONDS));
- private final ExpectedException thrown = ExpectedException.none();
-
@Rule
- public final TestRule chain = outerRule(thrown).around(k3po).around(timeout);
+ public final TestRule chain = outerRule(k3po).around(timeout);
@Test
@Specification({
@@ -237,8 +225,6 @@ public void shouldReceiveClientSentDataWithExtension() throws Exception
})
public void shouldRejectClientSentDataMissingExtension() throws Exception
{
- thrown.expect(hasProperty("failures", contains(asList(instanceOf(ComparisonFailure.class),
- instanceOf(TestTimedOutException.class)))));
k3po.finish();
}
@@ -310,8 +296,6 @@ public void shouldReceiveServerSentDataWithExtension() throws Exception
})
public void shouldRejectServerSentDataWithMissingExtension() throws Exception
{
- thrown.expect(anyOf(isA(ComparisonFailure.class),
- hasProperty("failures", hasItem(isA(ComparisonFailure.class)))));
k3po.finish();
}
@@ -614,8 +598,6 @@ public void shouldReceiveServerFlushedNullDataWithExtensionUsingExpression() thr
})
public void shouldReportFailureFromReadNullDataWhenNullDataIsNotWritten() throws Exception
{
- thrown.expect(anyOf(isA(ComparisonFailure.class),
- hasProperty("failures", hasItem(isA(ComparisonFailure.class)))));
k3po.finish();
}
@@ -778,5 +760,4 @@ public void shouldReceiveServerSentWriteAbortWithExt() throws Exception
{
k3po.finish();
}
-
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/TestBindingFactory.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/TestBindingFactory.java
index d6095c2bbf..bda05ef71d 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/TestBindingFactory.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/TestBindingFactory.java
@@ -17,6 +17,7 @@
import static io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfigAdapter.DEFAULT_ASSERTION_SCHEMA;
+import java.security.KeyStore;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -40,6 +41,7 @@
import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig;
import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig.CatalogAssertion;
import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig.Event;
+import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig.VaultAssertion;
import io.aklivity.zilla.runtime.engine.test.internal.event.TestEventContext;
import io.aklivity.zilla.runtime.engine.test.internal.k3po.ext.types.OctetsFW;
import io.aklivity.zilla.runtime.engine.test.internal.k3po.ext.types.stream.AbortFW;
@@ -50,6 +52,7 @@
import io.aklivity.zilla.runtime.engine.test.internal.k3po.ext.types.stream.FlushFW;
import io.aklivity.zilla.runtime.engine.test.internal.k3po.ext.types.stream.ResetFW;
import io.aklivity.zilla.runtime.engine.test.internal.k3po.ext.types.stream.WindowFW;
+import io.aklivity.zilla.runtime.engine.vault.VaultHandler;
final class TestBindingFactory implements BindingHandler
{
@@ -89,6 +92,8 @@ final class TestBindingFactory implements BindingHandler
private String credentials;
private List events;
private int eventIndex;
+ private VaultHandler vault;
+ private VaultAssertion vaultAssertion;
TestBindingFactory(
EngineContext context)
@@ -143,6 +148,12 @@ public void attach(
}
this.events = options.events;
+
+ if (binding.vault != null)
+ {
+ this.vault = context.supplyVault(binding.vaultId);
+ this.vaultAssertion = options.vaultAssertion;
+ }
}
}
@@ -267,6 +278,28 @@ private void onInitialBegin(
target.doInitialBegin(traceId);
+ if (vault != null && vaultAssertion != null)
+ {
+ String key = vaultAssertion.key;
+ if (key != null)
+ {
+ vault.initKeys(List.of(key));
+ }
+
+ String signer = vaultAssertion.signer;
+ if (signer != null)
+ {
+ vault.initSigners(List.of(signer));
+ }
+
+ String trust = vaultAssertion.trust;
+ if (trust != null)
+ {
+ KeyStore cacerts = null;
+ vault.initTrust(List.of(trust), cacerts);
+ }
+ }
+
if (catalogs != null)
{
CatalogHandler handler = catalogs.get(0);
@@ -324,10 +357,12 @@ else if (assertion.schema != null && !assertion.schema.equals(schema))
}
}
}
+
if (guard != null)
{
guard.reauthorize(traceId, routedId, 0, credentials);
}
+
while (events != null && eventIndex < events.size())
{
Event e = events.get(eventIndex);
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfig.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfig.java
index 6d29ec7be3..7a138648bb 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfig.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfig.java
@@ -30,6 +30,7 @@ public final class TestBindingOptionsConfig extends OptionsConfig
public final List cataloged;
public final List events;
public final List catalogAssertions;
+ public final VaultAssertion vaultAssertion;
public static TestBindingOptionsConfigBuilder builder()
{
@@ -48,7 +49,8 @@ public static TestBindingOptionsConfigBuilder builder(
TestAuthorizationConfig authorization,
List cataloged,
List events,
- List catalogAssertions)
+ List catalogAssertions,
+ VaultAssertion vaultAssertion)
{
super(value != null ? List.of(value) : List.of(), List.of());
this.value = value;
@@ -57,6 +59,7 @@ public static TestBindingOptionsConfigBuilder builder(
this.cataloged = cataloged;
this.events = events;
this.catalogAssertions = catalogAssertions;
+ this.vaultAssertion = vaultAssertion;
}
public static final class Event
@@ -73,6 +76,24 @@ public Event(
}
}
+ public static final class VaultAssertion
+ {
+ public final String key;
+ public final String signer;
+ public final String trust;
+
+ public VaultAssertion(
+ String key,
+ String signer,
+ String trust)
+ {
+ this.key = key;
+ this.signer = signer;
+ this.trust = trust;
+ }
+ }
+
+
public static final class CatalogAssertions
{
public final String name;
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigAdapter.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigAdapter.java
index af9bd385e2..70c4a1b6d8 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigAdapter.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigAdapter.java
@@ -31,6 +31,7 @@
import io.aklivity.zilla.runtime.engine.config.OptionsConfigAdapterSpi;
import io.aklivity.zilla.runtime.engine.config.SchemaConfig;
import io.aklivity.zilla.runtime.engine.config.SchemaConfigAdapter;
+import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig.VaultAssertion;
public final class TestBindingOptionsConfigAdapter implements OptionsConfigAdapterSpi
{
@@ -48,6 +49,10 @@ public final class TestBindingOptionsConfigAdapter implements OptionsConfigAdapt
private static final String ID_NAME = "id";
private static final String SCHEMA_NAME = "schema";
private static final String DELAY_NAME = "delay";
+ private static final String VAULT_NAME = "vault";
+ private static final String VAULT_KEY_NAME = "key";
+ private static final String VAULT_SIGNER_NAME = "signer";
+ private static final String VAULT_TRUST_NAME = "trust";
private final ModelConfigAdapter model = new ModelConfigAdapter();
@@ -98,24 +103,41 @@ public JsonObject adaptToJson(
object.add(CATALOG_NAME, catalogs);
}
- if (testOptions.catalogAssertions != null)
+ if (testOptions.catalogAssertions != null ||
+ testOptions.vaultAssertion != null)
{
JsonObjectBuilder assertions = Json.createObjectBuilder();
- JsonObjectBuilder catalogAssertions = Json.createObjectBuilder();
- for (TestBindingOptionsConfig.CatalogAssertions c : testOptions.catalogAssertions)
+
+ if (testOptions.catalogAssertions != null)
{
- JsonArrayBuilder array = Json.createArrayBuilder();
- for (TestBindingOptionsConfig.CatalogAssertion a: c.assertions)
+ JsonObjectBuilder catalogAssertions = Json.createObjectBuilder();
+ for (TestBindingOptionsConfig.CatalogAssertions c : testOptions.catalogAssertions)
{
- JsonObjectBuilder assertion = Json.createObjectBuilder();
- assertion.add(ID_NAME, a.id);
- assertion.add(SCHEMA_NAME, a.schema);
- assertion.add(DELAY_NAME, a.delay);
- array.add(assertion);
+ JsonArrayBuilder array = Json.createArrayBuilder();
+ for (TestBindingOptionsConfig.CatalogAssertion a: c.assertions)
+ {
+ JsonObjectBuilder assertion = Json.createObjectBuilder();
+ assertion.add(ID_NAME, a.id);
+ assertion.add(SCHEMA_NAME, a.schema);
+ assertion.add(DELAY_NAME, a.delay);
+ array.add(assertion);
+ }
+ catalogAssertions.add(c.name, array);
}
- catalogAssertions.add(c.name, array);
+ assertions.add(CATALOG_NAME, catalogAssertions);
}
- assertions.add(CATALOG_NAME, catalogAssertions);
+
+ if (testOptions.vaultAssertion != null)
+ {
+ VaultAssertion v = testOptions.vaultAssertion;
+ JsonObjectBuilder assertion = Json.createObjectBuilder()
+ .add(VAULT_KEY_NAME, v.key)
+ .add(VAULT_SIGNER_NAME, v.signer)
+ .add(VAULT_TRUST_NAME, v.trust);
+
+ assertions.add(VAULT_NAME, assertion);
+ }
+
object.add(ASSERTIONS_NAME, assertions);
}
@@ -184,6 +206,7 @@ public OptionsConfig adaptFromJson(
if (object.containsKey(ASSERTIONS_NAME))
{
JsonObject assertionsJson = object.getJsonObject(ASSERTIONS_NAME);
+
if (assertionsJson.containsKey(CATALOG_NAME))
{
JsonObject catalogsJson = assertionsJson.getJsonObject(CATALOG_NAME);
@@ -203,6 +226,15 @@ public OptionsConfig adaptFromJson(
testOptions.catalogAssertions(catalogName, catalogAssertions);
}
}
+
+ if (assertionsJson.containsKey(VAULT_NAME))
+ {
+ JsonObject vaultJson = assertionsJson.getJsonObject(VAULT_NAME);
+ testOptions.vaultAssertion(new TestBindingOptionsConfig.VaultAssertion(
+ vaultJson.containsKey(VAULT_KEY_NAME) ? vaultJson.getString(VAULT_KEY_NAME) : null,
+ vaultJson.containsKey(VAULT_SIGNER_NAME) ? vaultJson.getString(VAULT_SIGNER_NAME) : null,
+ vaultJson.containsKey(VAULT_TRUST_NAME) ? vaultJson.getString(VAULT_TRUST_NAME) : null));
+ }
}
if (object.containsKey(AUTHORIZATION_NAME))
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigBuilder.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigBuilder.java
index 56c048b4ae..a21a898af7 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigBuilder.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/binding/config/TestBindingOptionsConfigBuilder.java
@@ -23,6 +23,7 @@
import io.aklivity.zilla.runtime.engine.config.ConfigBuilder;
import io.aklivity.zilla.runtime.engine.config.ModelConfig;
import io.aklivity.zilla.runtime.engine.config.OptionsConfig;
+import io.aklivity.zilla.runtime.engine.test.internal.binding.config.TestBindingOptionsConfig.VaultAssertion;
public final class TestBindingOptionsConfigBuilder extends ConfigBuilder>
{
@@ -34,6 +35,7 @@ public final class TestBindingOptionsConfigBuilder extends ConfigBuilder catalogs;
private List events;
private List catalogAssertions;
+ private VaultAssertion vaultAssertion;
TestBindingOptionsConfigBuilder(
Function mapper)
@@ -101,9 +103,17 @@ public TestBindingOptionsConfigBuilder catalogAssertions(
return this;
}
+ public TestBindingOptionsConfigBuilder vaultAssertion(
+ TestBindingOptionsConfig.VaultAssertion assertion)
+ {
+ this.vaultAssertion = assertion;
+ return this;
+ }
+
@Override
public T build()
{
- return mapper.apply(new TestBindingOptionsConfig(value, mode, authorization, catalogs, events, catalogAssertions));
+ return mapper.apply(new TestBindingOptionsConfig(value, mode, authorization, catalogs, events,
+ catalogAssertions, vaultAssertion));
}
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/TestVaultHandler.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/TestVaultHandler.java
index b04ec5739b..8e684927f9 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/TestVaultHandler.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/TestVaultHandler.java
@@ -15,37 +15,173 @@
*/
package io.aklivity.zilla.runtime.engine.test.internal.vault;
-import java.security.KeyStore.PrivateKeyEntry;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.KeyStore;
import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.agrona.LangUtil;
import io.aklivity.zilla.runtime.engine.config.VaultConfig;
+import io.aklivity.zilla.runtime.engine.test.internal.vault.config.TestVaultEntryConfig;
+import io.aklivity.zilla.runtime.engine.test.internal.vault.config.TestVaultOptionsConfig;
import io.aklivity.zilla.runtime.engine.vault.VaultHandler;
public final class TestVaultHandler implements VaultHandler
{
+ private static final Pattern PATTERN_KEY_ENTRY =
+ Pattern.compile(
+ "(?-----BEGIN PRIVATE KEY-----\n[^-]+-----END PRIVATE KEY-----\n)" +
+ "(?(?:-----BEGIN CERTIFICATE-----\n[^-]+-----END CERTIFICATE-----\n)+)");
+
+ private final TestVaultEntryConfig key;
+ private final TestVaultEntryConfig signer;
+ private final TestVaultEntryConfig trust;
+
public TestVaultHandler(
VaultConfig vault)
{
+ TestVaultOptionsConfig options = (TestVaultOptionsConfig) vault.options;
+ this.key = options != null ? options.key : null;
+ this.signer = options != null ? options.signer : null;
+ this.trust = options != null ? options.trust : null;
}
@Override
- public PrivateKeyEntry key(
- String name)
+ public KeyManagerFactory initKeys(
+ List aliases)
{
- return null;
+ KeyManagerFactory factory = null;
+
+ if (aliases != null && key != null && aliases.contains(key.alias))
+ {
+ final Matcher matchKey = PATTERN_KEY_ENTRY.matcher(key.entry);
+ if (matchKey.matches())
+ {
+ String encodedKey = matchKey.group("key");
+ String encodedChain = matchKey.group("chain");
+
+ try
+ {
+ CertificateFactory x509 = CertificateFactory.getInstance("X509");
+
+ InputStream exportedBytes = new ByteArrayInputStream(encodedChain.getBytes(US_ASCII));
+
+ Certificate[] chain = x509.generateCertificates(exportedBytes).toArray(Certificate[]::new);
+
+ String base64 = encodedKey
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replaceAll(System.lineSeparator(), "")
+ .replace("-----END PRIVATE KEY-----", "");
+ byte[] encoded = Base64.getMimeDecoder().decode(base64);
+
+ KeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
+ KeyFactory rsa = KeyFactory.getInstance("RSA");
+ PrivateKey rsaKey = rsa.generatePrivate(keySpec);
+
+ KeyStore.PrivateKeyEntry entry = new KeyStore.PrivateKeyEntry(rsaKey, chain);
+
+ KeyStore store = KeyStore.getInstance("PKCS12");
+ KeyStore.PasswordProtection protection = new KeyStore.PasswordProtection("test".toCharArray());
+ store.load(null, protection.getPassword());
+
+ store.setEntry(key.alias, entry, protection);
+
+ factory = KeyManagerFactory.getInstance("PKIX");
+ factory.init(store, protection.getPassword());
+ }
+ catch (Exception ex)
+ {
+ LangUtil.rethrowUnchecked(ex);
+ }
+ }
+ }
+
+ return factory;
}
@Override
- public TrustedCertificateEntry certificate(
- String name)
+ public KeyManagerFactory initSigners(
+ List aliases)
{
- return null;
+ KeyManagerFactory factory = null;
+
+ if (aliases != null && signer != null && aliases.contains(signer.alias))
+ {
+ if (key != null && key.entry.contains(signer.entry))
+ {
+ factory = initKeys(List.of(key.alias));
+ }
+ }
+
+ return factory;
}
@Override
- public PrivateKeyEntry[] keys(
- String name)
+ public TrustManagerFactory initTrust(
+ List certAliases,
+ KeyStore cacerts)
{
- return null;
+ TrustManagerFactory factory = null;
+
+ if (certAliases != null && trust != null && certAliases.contains(trust.alias) ||
+ cacerts != null)
+ {
+ try
+ {
+ KeyStore store = KeyStore.getInstance("PKCS12");
+ store.load(null, null);
+
+ if (certAliases != null && trust != null && certAliases.contains(trust.alias))
+ {
+ CertificateFactory x509 = CertificateFactory.getInstance("X509");
+
+ InputStream certificateBytes = new ByteArrayInputStream(trust.entry.getBytes(US_ASCII));
+ Certificate certificate = x509.generateCertificate(certificateBytes);
+
+ KeyStore.TrustedCertificateEntry entry = new KeyStore.TrustedCertificateEntry(certificate);
+
+ store.setEntry(trust.alias, entry, null);
+ }
+
+ if (cacerts != null)
+ {
+ List aliases = Collections.list(cacerts.aliases());
+ for (String alias : aliases)
+ {
+ if (cacerts.isCertificateEntry(alias) &&
+ cacerts.getEntry(alias, null) instanceof TrustedCertificateEntry cacert)
+ {
+ store.setEntry(alias, cacert, null);
+ }
+ }
+ }
+
+ factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(store);
+ }
+ catch (Exception ex)
+ {
+ LangUtil.rethrowUnchecked(ex);
+ }
+ }
+
+ return factory;
}
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfig.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfig.java
new file mode 100644
index 0000000000..e9a808fde4
--- /dev/null
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfig.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.engine.test.internal.vault.config;
+
+import java.util.function.Function;
+
+import io.aklivity.zilla.runtime.engine.config.OptionsConfig;
+
+public final class TestVaultEntryConfig extends OptionsConfig
+{
+ public final String alias;
+ public final String entry;
+
+ public static TestVaultOptionsConfigBuilder builder()
+ {
+ return new TestVaultOptionsConfigBuilder<>(TestVaultEntryConfig.class::cast);
+ }
+
+ public static TestVaultOptionsConfigBuilder builder(
+ Function mapper)
+ {
+ return new TestVaultOptionsConfigBuilder<>(mapper);
+ }
+
+ TestVaultEntryConfig(
+ String alias,
+ String entry)
+ {
+ this.alias = alias;
+ this.entry = entry;
+ }
+}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfigAdapter.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfigAdapter.java
new file mode 100644
index 0000000000..bc080ac003
--- /dev/null
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultEntryConfigAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021-2023 Aklivity Inc.
+ *
+ * Aklivity licenses this file to you 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:
+ *
+ * http://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 io.aklivity.zilla.runtime.engine.test.internal.vault.config;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public final class TestVaultEntryConfigAdapter implements JsonbAdapter
+{
+ private static final String ALIAS_NAME = "alias";
+ private static final String ENTRY_NAME = "entry";
+
+ @Override
+ public JsonObject adaptToJson(
+ TestVaultEntryConfig config)
+ {
+ JsonObjectBuilder object = Json.createObjectBuilder();
+ object.add(ALIAS_NAME, config.alias);
+ object.add(ENTRY_NAME, config.entry);
+ return object.build();
+ }
+
+ @Override
+ public TestVaultEntryConfig adaptFromJson(
+ JsonObject object)
+ {
+ TestVaultEntryConfig config = null;
+
+ if (object != null)
+ {
+ String alias = object.getString(ALIAS_NAME);
+ String entry = object.getString(ENTRY_NAME);
+
+ config = new TestVaultEntryConfig(alias, entry);
+ }
+
+ return config;
+ }
+}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfig.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfig.java
index a763bb3b0c..e76a8cc79c 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfig.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfig.java
@@ -21,7 +21,9 @@
public final class TestVaultOptionsConfig extends OptionsConfig
{
- public final String mode;
+ public final TestVaultEntryConfig key;
+ public final TestVaultEntryConfig signer;
+ public final TestVaultEntryConfig trust;
public static TestVaultOptionsConfigBuilder builder()
{
@@ -35,8 +37,12 @@ public static TestVaultOptionsConfigBuilder builder(
}
TestVaultOptionsConfig(
- String mode)
+ TestVaultEntryConfig key,
+ TestVaultEntryConfig signer,
+ TestVaultEntryConfig trust)
{
- this.mode = mode;
+ this.key = key;
+ this.signer = signer;
+ this.trust = trust;
}
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigAdapter.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigAdapter.java
index 46eac521ba..0604c2956b 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigAdapter.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigAdapter.java
@@ -24,7 +24,11 @@
public final class TestVaultOptionsConfigAdapter implements OptionsConfigAdapterSpi
{
- private static final String MODE_NAME = "mode";
+ private static final String KEY_NAME = "key";
+ private static final String SIGNER_NAME = "signer";
+ private static final String TRUST_NAME = "trust";
+
+ private final TestVaultEntryConfigAdapter entry = new TestVaultEntryConfigAdapter();
@Override
public Kind kind()
@@ -40,13 +44,26 @@ public String type()
@Override
public JsonObject adaptToJson(
- OptionsConfig options)
+ OptionsConfig adaptable)
{
- TestVaultOptionsConfig testOptions = (TestVaultOptionsConfig) options;
+ TestVaultOptionsConfig options = (TestVaultOptionsConfig) adaptable;
JsonObjectBuilder object = Json.createObjectBuilder();
- object.add(MODE_NAME, testOptions.mode);
+ if (options.key != null)
+ {
+ object.add(KEY_NAME, entry.adaptToJson(options.key));
+ }
+
+ if (options.signer != null)
+ {
+ object.add(SIGNER_NAME, entry.adaptToJson(options.signer));
+ }
+
+ if (options.trust != null)
+ {
+ object.add(TRUST_NAME, entry.adaptToJson(options.trust));
+ }
return object.build();
}
@@ -55,16 +72,32 @@ public JsonObject adaptToJson(
public OptionsConfig adaptFromJson(
JsonObject object)
{
- TestVaultOptionsConfigBuilder testOptions = TestVaultOptionsConfig.builder();
+ TestVaultOptionsConfigBuilder options = TestVaultOptionsConfig.builder();
if (object != null)
{
- if (object.containsKey(MODE_NAME))
+ if (object.containsKey(KEY_NAME))
+ {
+ JsonObject key = object.getJsonObject(KEY_NAME);
+ TestVaultEntryConfig config = entry.adaptFromJson(key);
+ options.key(config.alias, config.entry);
+ }
+
+ if (object.containsKey(SIGNER_NAME))
+ {
+ JsonObject signer = object.getJsonObject(SIGNER_NAME);
+ TestVaultEntryConfig config = entry.adaptFromJson(signer);
+ options.signer(config.alias, config.entry);
+ }
+
+ if (object.containsKey(TRUST_NAME))
{
- testOptions.mode(object.getString(MODE_NAME));
+ JsonObject trust = object.getJsonObject(TRUST_NAME);
+ TestVaultEntryConfig config = entry.adaptFromJson(trust);
+ options.trust(config.alias, config.entry);
}
}
- return testOptions.build();
+ return options.build();
}
}
diff --git a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigBuilder.java b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigBuilder.java
index 35a2545b8e..c18d05ead3 100644
--- a/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigBuilder.java
+++ b/runtime/engine/src/test/java/io/aklivity/zilla/runtime/engine/test/internal/vault/config/TestVaultOptionsConfigBuilder.java
@@ -24,7 +24,9 @@ public final class TestVaultOptionsConfigBuilder extends ConfigBuilder mapper;
- private String mode;
+ private TestVaultEntryConfig key;
+ private TestVaultEntryConfig signer;
+ private TestVaultEntryConfig trust;
TestVaultOptionsConfigBuilder(
Function mapper)
@@ -39,16 +41,33 @@ protected Class> thisType()
return (Class>) getClass();
}
- public TestVaultOptionsConfigBuilder mode(
- String mode)
+ public TestVaultOptionsConfigBuilder key(
+ String alias,
+ String entry)
{
- this.mode = mode;
+ key = new TestVaultEntryConfig(alias, entry);
+ return this;
+ }
+
+ public TestVaultOptionsConfigBuilder signer(
+ String alias,
+ String entry)
+ {
+ signer = new TestVaultEntryConfig(alias, entry);
+ return this;
+ }
+
+ public TestVaultOptionsConfigBuilder trust(
+ String alias,
+ String entry)
+ {
+ trust = new TestVaultEntryConfig(alias, entry);
return this;
}
@Override
public T build()
{
- return mapper.apply(new TestVaultOptionsConfig(mode));
+ return mapper.apply(new TestVaultOptionsConfig(key, signer, trust));
}
}
diff --git a/runtime/exporter-otlp/pom.xml b/runtime/exporter-otlp/pom.xml
index 50b3e73eda..e5c35e9732 100644
--- a/runtime/exporter-otlp/pom.xml
+++ b/runtime/exporter-otlp/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/exporter-prometheus/pom.xml b/runtime/exporter-prometheus/pom.xml
index 7feb03c1a9..d65b74dfb5 100644
--- a/runtime/exporter-prometheus/pom.xml
+++ b/runtime/exporter-prometheus/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/exporter-prometheus/src/test/java/io/aklivity/zilla/runtime/exporter/prometheus/internal/printer/PrometheusMetricsPrinterTest.java b/runtime/exporter-prometheus/src/test/java/io/aklivity/zilla/runtime/exporter/prometheus/internal/printer/PrometheusMetricsPrinterTest.java
index a15f4cfd4d..8114d6013b 100644
--- a/runtime/exporter-prometheus/src/test/java/io/aklivity/zilla/runtime/exporter/prometheus/internal/printer/PrometheusMetricsPrinterTest.java
+++ b/runtime/exporter-prometheus/src/test/java/io/aklivity/zilla/runtime/exporter/prometheus/internal/printer/PrometheusMetricsPrinterTest.java
@@ -14,8 +14,8 @@
*/
package io.aklivity.zilla.runtime.exporter.prometheus.internal.printer;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/runtime/exporter-stdout/pom.xml b/runtime/exporter-stdout/pom.xml
index c6945613c2..1b0e7ae07b 100644
--- a/runtime/exporter-stdout/pom.xml
+++ b/runtime/exporter-stdout/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/filesystem-http/pom.xml b/runtime/filesystem-http/pom.xml
index 1846fd7803..71fa587ce0 100644
--- a/runtime/filesystem-http/pom.xml
+++ b/runtime/filesystem-http/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/filesystem-http/src/main/java/io/aklivity/zilla/runtime/filesystem/http/internal/HttpFileSystemProvider.java b/runtime/filesystem-http/src/main/java/io/aklivity/zilla/runtime/filesystem/http/internal/HttpFileSystemProvider.java
index 14587c81f9..7f1038003e 100644
--- a/runtime/filesystem-http/src/main/java/io/aklivity/zilla/runtime/filesystem/http/internal/HttpFileSystemProvider.java
+++ b/runtime/filesystem-http/src/main/java/io/aklivity/zilla/runtime/filesystem/http/internal/HttpFileSystemProvider.java
@@ -52,7 +52,6 @@ public String getScheme()
}
@Override
- @SuppressWarnings("resource")
public FileSystem newFileSystem(
URI uri,
Map env)
diff --git a/runtime/guard-jwt/pom.xml b/runtime/guard-jwt/pom.xml
index 9ba34aad94..37a3849abf 100644
--- a/runtime/guard-jwt/pom.xml
+++ b/runtime/guard-jwt/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/metrics-grpc/pom.xml b/runtime/metrics-grpc/pom.xml
index 9f7dca27b4..17ff8f87e9 100644
--- a/runtime/metrics-grpc/pom.xml
+++ b/runtime/metrics-grpc/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/metrics-http/pom.xml b/runtime/metrics-http/pom.xml
index d6021f6d40..32ad5ec421 100644
--- a/runtime/metrics-http/pom.xml
+++ b/runtime/metrics-http/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/metrics-stream/pom.xml b/runtime/metrics-stream/pom.xml
index 5b1d535481..6592581d19 100644
--- a/runtime/metrics-stream/pom.xml
+++ b/runtime/metrics-stream/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/model-avro/pom.xml b/runtime/model-avro/pom.xml
index f85dec12bb..29f6c8c484 100644
--- a/runtime/model-avro/pom.xml
+++ b/runtime/model-avro/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroModelHandler.java b/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroModelHandler.java
index 9f171c8e8b..d0dcca7602 100644
--- a/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroModelHandler.java
+++ b/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroModelHandler.java
@@ -153,6 +153,8 @@ protected final boolean validate(
progress = index;
extractFields(buffer, index + length, schema);
break;
+ default:
+ break;
}
}
catch (IOException | AvroRuntimeException ex)
@@ -395,6 +397,8 @@ private void extract(
}
progress += fixedSize;
break;
+ default:
+ break;
}
}
diff --git a/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroWriteConverterHandler.java b/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroWriteConverterHandler.java
index d0aabfc388..68617a1ecb 100644
--- a/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroWriteConverterHandler.java
+++ b/runtime/model-avro/src/main/java/io/aklivity/zilla/runtime/model/avro/internal/AvroWriteConverterHandler.java
@@ -113,6 +113,8 @@ record = reader.read(record, decoderFactory.jsonDecoder(schema, in));
}
}
break;
+ default:
+ break;
}
}
catch (IOException | AvroRuntimeException ex)
diff --git a/runtime/model-core/pom.xml b/runtime/model-core/pom.xml
index 9173ea166b..0085b7d53a 100644
--- a/runtime/model-core/pom.xml
+++ b/runtime/model-core/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/model-json/pom.xml b/runtime/model-json/pom.xml
index 0714c4c483..54a6ec2f2c 100644
--- a/runtime/model-json/pom.xml
+++ b/runtime/model-json/pom.xml
@@ -6,7 +6,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/model-json/src/main/java/io/aklivity/zilla/runtime/model/json/internal/JsonModelHandler.java b/runtime/model-json/src/main/java/io/aklivity/zilla/runtime/model/json/internal/JsonModelHandler.java
index 95dd285404..88119dde9c 100644
--- a/runtime/model-json/src/main/java/io/aklivity/zilla/runtime/model/json/internal/JsonModelHandler.java
+++ b/runtime/model-json/src/main/java/io/aklivity/zilla/runtime/model/json/internal/JsonModelHandler.java
@@ -132,6 +132,8 @@ protected final boolean validate(
valueBytes = null;
}
break;
+ default:
+ break;
}
}
}
diff --git a/runtime/model-protobuf/pom.xml b/runtime/model-protobuf/pom.xml
index aa08194efa..a04cdf181c 100644
--- a/runtime/model-protobuf/pom.xml
+++ b/runtime/model-protobuf/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/model-protobuf/src/main/java/io/aklivity/zilla/runtime/model/protobuf/internal/ProtobufReadConverterHandler.java b/runtime/model-protobuf/src/main/java/io/aklivity/zilla/runtime/model/protobuf/internal/ProtobufReadConverterHandler.java
index 3bdd7e336e..65de751711 100644
--- a/runtime/model-protobuf/src/main/java/io/aklivity/zilla/runtime/model/protobuf/internal/ProtobufReadConverterHandler.java
+++ b/runtime/model-protobuf/src/main/java/io/aklivity/zilla/runtime/model/protobuf/internal/ProtobufReadConverterHandler.java
@@ -336,6 +336,8 @@ private void extract(
field.value.wrap(text, 0, length);
}
break;
+ default:
+ break;
}
}
diff --git a/runtime/pom.xml b/runtime/pom.xml
index e841fc140a..5078a53d5d 100644
--- a/runtime/pom.xml
+++ b/runtime/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/resolver-env/pom.xml b/runtime/resolver-env/pom.xml
index 1d2058e062..3226117e88 100644
--- a/runtime/resolver-env/pom.xml
+++ b/runtime/resolver-env/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/runtime/vault-filesystem/pom.xml b/runtime/vault-filesystem/pom.xml
index 632cd11525..d93a25ff3d 100644
--- a/runtime/vault-filesystem/pom.xml
+++ b/runtime/vault-filesystem/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
runtime
- 0.9.92
+ 0.9.93
../pom.xml
@@ -24,7 +24,7 @@
- 0.89
+ 0.84
0
diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java
index 80fc4d3fbc..eb5a4fdaf4 100644
--- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java
+++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java
@@ -18,6 +18,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
@@ -27,10 +28,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
+import java.util.function.BiFunction;
import java.util.function.Function;
-import java.util.function.Predicate;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
import org.agrona.LangUtil;
@@ -41,211 +45,276 @@
public class FileSystemVaultHandler implements VaultHandler
{
- private static final String TYPE_DEFAULT = "pkcs12";
+ private static final String STORE_TYPE_DEFAULT = "pkcs12";
- private final Function lookupKey;
- private final Function lookupTrust;
- private final Function lookupSigner;
- private final Function, KeyStore.PrivateKeyEntry[]> lookupKeys;
+ private final Function, KeyManagerFactory> supplyKeys;
+ private final Function, KeyManagerFactory> supplySigners;
+ private final BiFunction, KeyStore, TrustManagerFactory> supplyTrust;
public FileSystemVaultHandler(
FileSystemOptionsConfig options,
Function resolvePath)
{
- lookupKey = supplyLookupPrivateKeyEntry(resolvePath, options.keys);
- lookupTrust = supplyLookupTrustedCertificateEntry(resolvePath, options.trust);
- lookupSigner = supplyLookupTrustedCertificateEntry(resolvePath, options.signers);
- lookupKeys = supplyLookupPrivateKeyEntries(resolvePath, options.keys);
+ FileSystemStoreInfo keys = supplyStoreInfo(resolvePath, options.keys);
+ supplyKeys = keys != null
+ ? keys::newKeysFactory
+ : aliases -> null;
+
+ FileSystemStoreInfo signers = supplyStoreInfo(resolvePath, options.signers);
+ supplySigners = signers != null && keys != null
+ ? aliases -> newSignersFactory(aliases, signers, keys)
+ : aliases -> null;
+
+ FileSystemStoreInfo trust = supplyStoreInfo(resolvePath, options.trust);
+ supplyTrust = (aliases, cacerts) -> newTrustFactory(trust, aliases, cacerts);
}
@Override
- public KeyStore.PrivateKeyEntry key(
- String alias)
+ public KeyManagerFactory initKeys(
+ List aliases)
{
- return lookupKey.apply(alias);
+ return supplyKeys.apply(aliases);
}
@Override
- public KeyStore.TrustedCertificateEntry certificate(
- String alias)
+ public TrustManagerFactory initTrust(
+ List aliases,
+ KeyStore cacerts)
{
- return lookupTrust.apply(alias);
+ return supplyTrust.apply(aliases, cacerts);
}
@Override
- public PrivateKeyEntry[] keys(
- String signer)
+ public KeyManagerFactory initSigners(
+ List aliases)
{
- KeyStore.PrivateKeyEntry[] keys = null;
+ return supplySigners.apply(aliases);
+ }
+
+ private static FileSystemStoreInfo supplyStoreInfo(
+ Function resolvePath,
+ FileSystemStoreConfig config)
+ {
+ FileSystemStoreInfo info = null;
- TrustedCertificateEntry trusted = lookupSigner.apply(signer);
- if (trusted != null)
+ if (config != null)
{
- Certificate certificate = trusted.getTrustedCertificate();
- if (certificate instanceof X509Certificate)
+ try
+ {
+ Path storePath = resolvePath.apply(config.store);
+ try (InputStream input = Files.newInputStream(storePath))
+ {
+ String type = Optional.ofNullable(config.type).orElse(STORE_TYPE_DEFAULT);
+ char[] password = Optional.ofNullable(config.password).map(String::toCharArray).orElse(null);
+
+ KeyStore store = KeyStore.getInstance(type);
+ store.load(input, password);
+
+ info = new FileSystemStoreInfo(store, password);
+ }
+ }
+ catch (Exception ex)
{
- X509Certificate x509 = (X509Certificate) certificate;
- X500Principal issuer = x509.getSubjectX500Principal();
- keys = lookupKeys.apply(issuer::equals);
+ LangUtil.rethrowUnchecked(ex);
}
}
- return keys;
+ return info;
}
- private static Function supplyLookupPrivateKeyEntry(
- Function resolvePath,
- FileSystemStoreConfig aliases)
+ private KeyManagerFactory newSignersFactory(
+ List aliases,
+ FileSystemStoreInfo signers,
+ FileSystemStoreInfo keys)
{
- return supplyLookupAlias(resolvePath, aliases, FileSystemVaultHandler::lookupPrivateKeyEntry);
- }
+ KeyManagerFactory factory = null;
- private static Function supplyLookupTrustedCertificateEntry(
- Function resolvePath,
- FileSystemStoreConfig aliases)
- {
- return supplyLookupAlias(resolvePath, aliases, FileSystemVaultHandler::lookupTrustedCertificateEntry);
+ if (aliases != null)
+ {
+ factory = keys.newKeysFactory(aliases.stream()
+ .map(signers::certificate)
+ .filter(Objects::nonNull)
+ .map(TrustedCertificateEntry::getTrustedCertificate)
+ .filter(X509Certificate.class::isInstance)
+ .map(X509Certificate.class::cast)
+ .map(X509Certificate::getSubjectX500Principal)
+ .map(keys::issuedKeys)
+ .filter(Objects::nonNull)
+ .flatMap(List::stream)
+ .toList());
+ }
+
+ return factory;
}
- private Function, KeyStore.PrivateKeyEntry[]> supplyLookupPrivateKeyEntries(
- Function resolvePath,
- FileSystemStoreConfig entries)
+ private TrustManagerFactory newTrustFactory(
+ FileSystemStoreInfo store,
+ List aliases,
+ KeyStore cacerts)
{
- Function, KeyStore.PrivateKeyEntry[]> lookupKeys = p -> null;
+ TrustManagerFactory factory = null;
- if (entries != null)
+ try
{
- try
+ if (aliases != null || cacerts != null)
{
- Path storePath = resolvePath.apply(entries.store);
- try (InputStream input = Files.newInputStream(storePath))
- {
- String type = Optional.ofNullable(entries.type).orElse(TYPE_DEFAULT);
- char[] password = Optional.ofNullable(entries.password).map(String::toCharArray).orElse(null);
-
- KeyStore store = KeyStore.getInstance(type);
- store.load(input, password);
- KeyStore.PasswordProtection protection = new KeyStore.PasswordProtection(password);
-
- List aliases = Collections.list(store.aliases());
+ KeyStore trust = KeyStore.getInstance(STORE_TYPE_DEFAULT);
+ trust.load(null, null);
- lookupKeys = matchSigner ->
+ if (aliases != null && store != null)
+ {
+ for (String alias : aliases)
{
- List keys = null;
-
- for (String alias : aliases)
+ TrustedCertificateEntry cert = store.certificate(alias);
+ if (cert != null)
{
- PrivateKeyEntry key = lookupPrivateKeyEntry(alias, store, protection);
- Certificate certificate = key.getCertificate();
- if (key != null &&
- certificate instanceof X509Certificate &&
- matchSigner.test(((X509Certificate) certificate).getIssuerX500Principal()))
- {
- if (keys == null)
- {
- keys = new ArrayList<>();
- }
-
- keys.add(key);
- }
+ trust.setEntry(alias, cert, null);
}
+ }
+ }
- return keys != null ? keys.toArray(KeyStore.PrivateKeyEntry[]::new) : null;
- };
+ if (cacerts != null)
+ {
+ for (String alias : aliases)
+ {
+ TrustedCertificateEntry cacert = FileSystemStoreInfo.certificate(cacerts, alias);
+ if (cacert != null)
+ {
+ trust.setEntry(alias, cacert, null);
+ }
+ }
}
+
+ factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(trust);
}
- catch (Exception ex)
- {
- LangUtil.rethrowUnchecked(ex);
- }
+ }
+ catch (Exception ex)
+ {
+ LangUtil.rethrowUnchecked(ex);
}
- return lookupKeys;
+ return factory;
}
- private static Function supplyLookupAlias(
- Function resolvePath,
- FileSystemStoreConfig aliases,
- Lookup lookup)
+ private static final class FileSystemStoreInfo
{
- Function lookupAlias = a -> null;
+ private final KeyStore store;
+ private final KeyStore.PasswordProtection protection;
- if (aliases != null)
+ private FileSystemStoreInfo(
+ KeyStore store,
+ char[] password)
+ {
+ this.store = store;
+ this.protection = password != null ? new KeyStore.PasswordProtection(password) : null;
+ }
+
+ private KeyManagerFactory newKeysFactory(
+ List aliases)
{
+ KeyManagerFactory factory = null;
+
try
{
- Path storePath = resolvePath.apply(aliases.store);
- try (InputStream input = Files.newInputStream(storePath))
+ if (aliases != null)
{
- String type = Optional.ofNullable(aliases.type).orElse(TYPE_DEFAULT);
- char[] password = Optional.ofNullable(aliases.password).map(String::toCharArray).orElse(null);
+ KeyStore keys = KeyStore.getInstance(STORE_TYPE_DEFAULT);
+ keys.load(null, protection.getPassword());
- KeyStore store = KeyStore.getInstance(type);
- store.load(input, password);
- KeyStore.PasswordProtection protection = new KeyStore.PasswordProtection(password);
+ for (String alias : aliases)
+ {
+ PrivateKeyEntry key = key(alias);
+ if (key != null)
+ {
+ keys.setEntry(alias, key, protection);
+ }
+ }
- lookupAlias = alias -> lookup.apply(alias, store, protection);
+ factory = KeyManagerFactory.getInstance("PKIX");
+ factory.init(keys, protection.getPassword());
}
}
catch (Exception ex)
{
LangUtil.rethrowUnchecked(ex);
}
+
+ return factory;
}
- return lookupAlias;
- }
+ private PrivateKeyEntry key(
+ String alias)
+ {
+ return entry(store, protection, alias, PrivateKeyEntry.class);
+ }
- private static KeyStore.Entry lookupEntry(
- String alias,
- KeyStore store,
- KeyStore.PasswordProtection protection)
- {
- KeyStore.Entry entry = null;
+ private TrustedCertificateEntry certificate(
+ String alias)
+ {
+ return entry(store, null, alias, TrustedCertificateEntry.class);
+ }
- try
+ private static TrustedCertificateEntry certificate(
+ KeyStore store,
+ String alias)
{
- entry = store.getEntry(alias, protection);
+ return entry(store, null, alias, TrustedCertificateEntry.class);
}
- catch (Exception ex)
+
+ private static T entry(
+ KeyStore store,
+ KeyStore.PasswordProtection protection,
+ String alias,
+ Class type)
{
+ T typed = null;
+
try
{
- entry = store.getEntry(alias, null);
+ Entry entry = store.getEntry(alias, protection);
+ if (type.isInstance(entry))
+ {
+ typed = type.cast(entry);
+ }
}
- catch (Exception e)
+ catch (GeneralSecurityException ex)
{
- e.addSuppressed(ex);
- LangUtil.rethrowUnchecked(e);
}
- }
-
- return entry;
- }
- private static KeyStore.PrivateKeyEntry lookupPrivateKeyEntry(
- String alias,
- KeyStore store,
- KeyStore.PasswordProtection protection)
- {
- Entry entry = lookupEntry(alias, store, protection);
+ return typed;
+ }
- return entry instanceof KeyStore.PrivateKeyEntry ? (KeyStore.PrivateKeyEntry) entry : null;
- }
+ private List issuedKeys(
+ X500Principal issuer)
+ {
+ List keys = null;
- private static KeyStore.TrustedCertificateEntry lookupTrustedCertificateEntry(
- String alias,
- KeyStore store,
- KeyStore.PasswordProtection protection)
- {
- Entry entry = lookupEntry(alias, store, protection);
+ try
+ {
+ for (String alias : Collections.list(store.aliases()))
+ {
+ PrivateKeyEntry key = key(alias);
+ Certificate certificate = key.getCertificate();
+ if (key != null &&
+ certificate instanceof X509Certificate &&
+ issuer.equals(((X509Certificate) certificate).getIssuerX500Principal()))
+ {
+ if (keys == null)
+ {
+ keys = new ArrayList<>();
+ }
- return entry instanceof KeyStore.TrustedCertificateEntry ? (KeyStore.TrustedCertificateEntry) entry : null;
- }
+ keys.add(alias);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // ignore
+ }
- @FunctionalInterface
- private interface Lookup
- {
- T apply(String alias, KeyStore store, KeyStore.PasswordProtection protection);
+ return keys;
+ }
}
}
diff --git a/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java b/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java
index 2f16fdcc88..e89864d3fe 100644
--- a/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java
+++ b/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java
@@ -16,15 +16,16 @@
package io.aklivity.zilla.runtime.vault.filesystem.internal;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
-import java.security.KeyStore.PrivateKeyEntry;
-import java.security.KeyStore.TrustedCertificateEntry;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
import org.junit.Test;
@@ -48,13 +49,13 @@ public void shouldResolveServer() throws Exception
.build()
.build();
- FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::getResourcePath);
+ FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::resourcePath);
- PrivateKeyEntry key = vault.key("localhost");
- TrustedCertificateEntry certificate = vault.certificate("clientca");
+ KeyManagerFactory keys = vault.initKeys(List.of("localhost"));
+ TrustManagerFactory trust = vault.initTrust(List.of("clientca"), null);
- assertThat(key, not(nullValue()));
- assertThat(certificate, not(nullValue()));
+ assertThat(keys, not(nullValue()));
+ assertThat(trust, not(nullValue()));
}
@Test
@@ -73,18 +74,14 @@ public void shouldResolveClient() throws Exception
.build()
.build();
- FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::getResourcePath);
+ FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::resourcePath);
- PrivateKeyEntry key = vault.key("client1");
- PrivateKeyEntry[] signedKeys = vault.keys("clientca");
+ KeyManagerFactory keys = vault.initSigners(List.of("clientca"));
- assertThat(key, not(nullValue()));
- assertThat(signedKeys, not(nullValue()));
- assertThat(signedKeys.length, equalTo(1));
- assertThat(signedKeys[0], not(nullValue()));
+ assertThat(keys, not(nullValue()));
}
- public static Path getResourcePath(
+ public static Path resourcePath(
String resource)
{
URL url = FileSystemVaultTest.class.getResource(resource);
diff --git a/specs/binding-asyncapi.spec/pom.xml b/specs/binding-asyncapi.spec/pom.xml
index 3d6213ffb2..4ff3a9a121 100644
--- a/specs/binding-asyncapi.spec/pom.xml
+++ b/specs/binding-asyncapi.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-echo.spec/pom.xml b/specs/binding-echo.spec/pom.xml
index 41d6663cf7..6efff6d540 100644
--- a/specs/binding-echo.spec/pom.xml
+++ b/specs/binding-echo.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-fan.spec/pom.xml b/specs/binding-fan.spec/pom.xml
index 78c5b1b136..a5b864d5a7 100644
--- a/specs/binding-fan.spec/pom.xml
+++ b/specs/binding-fan.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-filesystem.spec/pom.xml b/specs/binding-filesystem.spec/pom.xml
index 5f5ee30721..7e012b68d1 100644
--- a/specs/binding-filesystem.spec/pom.xml
+++ b/specs/binding-filesystem.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-grpc-kafka.spec/pom.xml b/specs/binding-grpc-kafka.spec/pom.xml
index 09c11d0481..c0f9720932 100644
--- a/specs/binding-grpc-kafka.spec/pom.xml
+++ b/specs/binding-grpc-kafka.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-grpc.spec/pom.xml b/specs/binding-grpc.spec/pom.xml
index b195722ef7..75bd27c01e 100644
--- a/specs/binding-grpc.spec/pom.xml
+++ b/specs/binding-grpc.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-http-filesystem.spec/pom.xml b/specs/binding-http-filesystem.spec/pom.xml
index 040853af7f..60722aac6f 100644
--- a/specs/binding-http-filesystem.spec/pom.xml
+++ b/specs/binding-http-filesystem.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-http-kafka.spec/pom.xml b/specs/binding-http-kafka.spec/pom.xml
index 128970de4f..c47e1fb4b8 100644
--- a/specs/binding-http-kafka.spec/pom.xml
+++ b/specs/binding-http-kafka.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-http.spec/pom.xml b/specs/binding-http.spec/pom.xml
index 649499cfbc..539c5e4660 100644
--- a/specs/binding-http.spec/pom.xml
+++ b/specs/binding-http.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.realm.yaml b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v1.1/server.with.route.header.overrides.yaml
similarity index 54%
rename from specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.realm.yaml
rename to specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v1.1/server.with.route.header.overrides.yaml
index 94ec4c8ba4..a51de67917 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.realm.yaml
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v1.1/server.with.route.header.overrides.yaml
@@ -16,34 +16,24 @@
---
name: test
-vaults:
- server:
- type: filesystem
- options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
- trust:
- store: stores/server/trust
- type: pkcs12
- password: generated
-realms:
- - name: clients
- type: tls
bindings:
net0:
- type: tls
+ type: http
kind: server
- vault: server
options:
- keys:
- - localhost
- trust:
- - clientca
- realm: clients
+ versions:
+ - http/1.1
+ overrides:
+ :authority: otherhost:8181
routes:
- exit: app0
- allow:
- realm: clients
- identity: client#1
+ when:
+ - headers:
+ :authority: localhost:8080
+ :path: /v1/{id}
+ with:
+ headers:
+ overrides:
+ :authority: localhost:8081
+ :path: /api/${params.id}
+ custom: value
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v2/server.with.route.header.overrides.yaml b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v2/server.with.route.header.overrides.yaml
new file mode 100644
index 0000000000..f6784c63c2
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/config/v2/server.with.route.header.overrides.yaml
@@ -0,0 +1,38 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+---
+name: test
+bindings:
+ net0:
+ type: http
+ kind: server
+ options:
+ versions:
+ - h2
+ overrides:
+ :authority: otherhost:8181
+ :path: /api/path
+ routes:
+ - exit: app0
+ when:
+ - headers:
+ :authority: localhost:8080
+ with:
+ headers:
+ overrides:
+ :authority: localhost:8081
+ custom: value
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/schema/http.schema.patch.json b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/schema/http.schema.patch.json
index 62f6124fe5..3976bd34a3 100644
--- a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/schema/http.schema.patch.json
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/schema/http.schema.patch.json
@@ -369,7 +369,31 @@
"additionalProperties": false
}
},
- "with": false
+ "with":
+ {
+ "properties":
+ {
+ "headers":
+ {
+ "title": "Headers",
+ "type": "object",
+ "properties":
+ {
+ "overrides":
+ {
+ "title": "Overrides",
+ "type": "object",
+ "additionalProperties" :
+ {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+ }
}
}
}
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/client.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/client.rpt
new file mode 100644
index 0000000000..f3cd822fa1
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/client.rpt
@@ -0,0 +1,39 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+write zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":scheme", "http")
+ .header(":method", "GET")
+ .header(":path", "/api/path")
+ .header(":authority", "localhost:8081")
+ .header("custom", "value")
+ .build()}
+connected
+
+write close
+
+read zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":status", "200")
+ .header("content-length", "0")
+ .build()}
+
+read closed
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/server.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/server.rpt
new file mode 100644
index 0000000000..75d69f34b8
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/connection.management/request.with.route.header.overrides/server.rpt
@@ -0,0 +1,41 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+accept "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+accepted
+
+read zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":scheme", "http")
+ .header(":method", "GET")
+ .header(":path", "/api/path")
+ .header(":authority", "localhost:8081")
+ .header("custom", "value")
+ .build()}
+connected
+
+read closed
+
+write zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":status", "200")
+ .header("content-length", "0")
+ .build()}
+write flush
+
+write close
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt
new file mode 100644
index 0000000000..02f2bd6704
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt
@@ -0,0 +1,38 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+
+write zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":method", "GET")
+ .header(":scheme", "http")
+ .header(":path", "/api/path")
+ .header(":authority", "localhost:8081")
+ .header("custom", "value")
+ .build()}
+connected
+
+read zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":status", "200")
+ .header("content-length", "0")
+ .build()}
+
+read closed
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt
new file mode 100644
index 0000000000..a0c65d666c
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt
@@ -0,0 +1,39 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+accept "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+accepted
+
+read zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":method", "GET")
+ .header(":scheme", "http")
+ .header(":path", "/api/path")
+ .header(":authority", "localhost:8081")
+ .header("custom", "value")
+ .build()}
+connected
+
+write zilla:begin.ext ${http:beginEx()
+ .typeId(zilla:id("http"))
+ .header(":status", "200")
+ .header("content-length", "0")
+ .build()}
+write flush
+
+write close
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/client.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/client.rpt
new file mode 100644
index 0000000000..6cd20a195d
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/client.rpt
@@ -0,0 +1,28 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+connect "zilla://streams/net0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+connected
+
+write "GET /v1/path HTTP/1.1" "\r\n"
+write "Host: localhost:8080" "\r\n"
+write "\r\n"
+
+read "HTTP/1.1 200 OK\r\n"
+read "Content-Length: 0\r\n"
+read "\r\n"
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/server.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/server.rpt
new file mode 100644
index 0000000000..3cf8c37041
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/connection.management/request.with.route.header.overrides/server.rpt
@@ -0,0 +1,29 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+accept "zilla://streams/net0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+accepted
+connected
+
+read "GET /v1/path HTTP/1.1" "\r\n"
+read "Host: localhost:8080" "\r\n"
+read "\r\n"
+
+write "HTTP/1.1 200 OK\r\n"
+write "Content-Length: 0\r\n"
+write "\r\n"
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt
new file mode 100644
index 0000000000..5811f2391e
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/client.rpt
@@ -0,0 +1,73 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+connect "zilla://streams/net0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+connected
+
+# client connection preface
+write "PRI * HTTP/2.0\r\n"
+ "\r\n"
+ "SM\r\n"
+ "\r\n"
+write flush
+
+# server connection preface - SETTINGS frame
+read [0x00 0x00 0x12] # length = 18
+ [0x04] # HTTP2 SETTINGS frame
+ [0x00] # flags = 0x00
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+ [0x00 0x03 0x00 0x00 0x00 0x64] # SETTINGS_MAX_CONCURRENT_STREAMS(0x03) = 100
+ [0x00 0x04 0x00 0x00 0x00 0x00] # SETTINGS_INITIAL_WINDOW_SIZE(0x04) = 0
+ [0x00 0x06 0x00 0x00 0x20 0x00] # SETTINGS_MAX_HEADER_LIST_SIZE(0x06) = 8192
+
+write [0x00 0x00 0x00] # length = 0
+ [0x04] # HTTP2 SETTINGS frame
+ [0x01] # ACK
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+write flush
+
+write [0x00 0x00 0x0c] # length = 12
+ [0x04] # HTTP2 SETTINGS frame
+ [0x00] # flags = 0x00
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+ [0x00 0x03 0x00 0x00 0x00 0x64] # SETTINGS_MAX_CONCURRENT_STREAMS(0x03) = 100
+ [0x00 0x04 0x00 0x00 0xff 0xff] # SETTINGS_INITIAL_WINDOW_SIZE(0x04) = 65535
+write flush
+
+write [0x00 0x00 0x13] # length = 19
+ [0x01] # HEADERS frame
+ [0x05] # END_HEADERS | END_STREAM
+ [0x00 0x00 0x00 0x01] # stream_id = 1
+ [0x82] # :method: GET
+ [0x86] # :scheme: http
+ [0x84] # :path: /
+ [0x01] [0x0e] "localhost:8080" # :authority: localhost:8080
+write flush
+
+read [0x00 0x00 0x00] # length = 0
+ [0x04] # HTTP2 SETTINGS frame
+ [0x01] # ACK
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+
+read [0x00 0x00 0x05] # length
+ [0x01] # HTTP2 HEADERS frame
+ [0x05] # END_HEADERS
+ [0x00 0x00 0x00 0x01] # stream_id=1
+ [0x88] # :status: 200
+ [0x0f 0x0d] [0x01] "0" # content-length
+
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt
new file mode 100644
index 0000000000..50f92937f9
--- /dev/null
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/connection.management/http.get.exchange.with.route.header.overrides/server.rpt
@@ -0,0 +1,72 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+accept "zilla://streams/net0"
+ option zilla:window 8192
+ option zilla:transmission "duplex"
+accepted
+connected
+
+# server connection preface - SETTINGS frame
+write [0x00 0x00 0x12] # length = 18
+ [0x04] # HTTP2 SETTINGS frame
+ [0x00] # flags = 0x00
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+ [0x00 0x03 0x00 0x00 0x00 0x64] # SETTINGS_MAX_CONCURRENT_STREAMS(0x03) = 100
+ [0x00 0x04 0x00 0x00 0x00 0x00] # SETTINGS_INITIAL_WINDOW_SIZE(0x03) = 0
+ [0x00 0x06 0x00 0x00 0x20 0x00] # SETTINGS_MAX_HEADER_LIST_SIZE(0x06) = 8192
+write flush
+
+# client connection preface
+read "PRI * HTTP/2.0\r\n"
+ "\r\n"
+ "SM\r\n"
+ "\r\n"
+
+read [0x00 0x00 0x0c] # length = 12
+ [0x04] # HTTP2 SETTINGS frame
+ [0x00] # flags = 0x00
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+ [0x00 0x03 0x00 0x00 0x00 0x64] # SETTINGS_MAX_CONCURRENT_STREAMS(0x03) = 100
+ [0x00 0x04 0x00 0x00 0xff 0xff] # SETTINGS_INITIAL_WINDOW_SIZE(0x04) = 65535
+
+write [0x00 0x00 0x00] # length = 0
+ [0x04] # HTTP2 SETTINGS frame
+ [0x01] # ACK
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+write flush
+
+read [0x00 0x00 0x13] # length = 19
+ [0x01] # HEADERS frame
+ [0x05] # END_HEADERS | END_STREAM
+ [0x00 0x00 0x00 0x01] # stream_id = 1
+ [0x82] # :method: GET
+ [0x86] # :scheme: http
+ [0x84] # :path: /
+ [0x01] [0x0e] "localhost:8080" # :authority: localhost:8080
+
+read [0x00 0x00 0x00] # length = 0
+ [0x04] # HTTP2 SETTINGS frame
+ [0x01] # ACK
+ [0x00 0x00 0x00 0x00] # stream_id = 0
+
+write [0x00 0x00 0x05] # length
+ [0x01] # HTTP2 HEADERS frame
+ [0x05] # END_HEADERS
+ [0x00 0x00 0x00 0x01] # stream_id=1
+ [0x88] # :status: 200
+ [0x0f 0x0d] [0x01] "0" # content-length
+write flush
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/client.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/client.rpt
index 0fd4d64948..d6cb5c9b1b 100644
--- a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/client.rpt
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/client.rpt
@@ -22,14 +22,16 @@ connected
write "POST / HTTP/1.1\r\n"
"Host: localhost:8080\r\n"
"Connection: Upgrade, HTTP2-Settings\r\n"
- "Upgrade: h2c\r\n"
"Content-Type: text/plain;charset=UTF-8\r\n"
"Content-Length: 12\r\n"
+ "Upgrade: h2c\r\n"
"\r\n"
write "Hello, world"
read "HTTP/1.1 200 OK\r\n"
+ "Server: CERN/3.0 libwww/2.17\r\n"
+ "Date: Wed, 01 Feb 2017 19:12:46 GMT\r\n"
"Content-Type: text/plain;charset=UTF-8\r\n"
"Content-Length: 17\r\n"
"\r\n"
diff --git a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/server.rpt b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/server.rpt
index 8b65fd60ee..e44eaf020e 100644
--- a/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/server.rpt
+++ b/specs/binding-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/starting/upgrade.h2c.with.no.settings/server.rpt
@@ -23,14 +23,16 @@ connected
read "POST / HTTP/1.1\r\n"
"Host: localhost:8080\r\n"
"Connection: Upgrade, HTTP2-Settings\r\n"
- "Upgrade: h2c\r\n"
"Content-Type: text/plain;charset=UTF-8\r\n"
"Content-Length: 12\r\n"
+ "Upgrade: h2c\r\n"
"\r\n"
read "Hello, world"
write "HTTP/1.1 200 OK\r\n"
+ "Server: CERN/3.0 libwww/2.17\r\n"
+ "Date: Wed, 01 Feb 2017 19:12:46 GMT\r\n"
"Content-Type: text/plain;charset=UTF-8\r\n"
"Content-Length: 17\r\n"
"\r\n"
diff --git a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/config/SchemaTest.java b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/config/SchemaTest.java
index 5299abbd37..7c96b584c8 100644
--- a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/config/SchemaTest.java
+++ b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/config/SchemaTest.java
@@ -83,6 +83,14 @@ public void shouldValidateHttp11ServerOverride()
assertThat(config, not(nullValue()));
}
+ @Test
+ public void shouldValidateHttp11ServerWithRouteHeaderOverrides()
+ {
+ JsonObject config = schema.validate("v1.1/server.with.route.header.overrides.yaml");
+
+ assertThat(config, not(nullValue()));
+ }
+
@Test
public void shouldValidateHttp11ServerAccessControlCrossOrigin()
{
@@ -211,6 +219,14 @@ public void shouldValidateHttp2ServerOverride()
assertThat(config, not(nullValue()));
}
+ @Test
+ public void shouldValidateHttp2ServerWithRouteHeaderOverrides()
+ {
+ JsonObject config = schema.validate("v2/server.with.route.header.overrides.yaml");
+
+ assertThat(config, not(nullValue()));
+ }
+
@Test
public void shouldValidateHttp2ServerAccessControlCrossOrigin()
{
diff --git a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/ConnectionManagementIT.java b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/ConnectionManagementIT.java
index 719b1c6abc..562e5f8999 100644
--- a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/ConnectionManagementIT.java
+++ b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7230/ConnectionManagementIT.java
@@ -303,6 +303,15 @@ public void shouldProxyRequestWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${app}/request.with.route.header.overrides/client",
+ "${app}/request.with.route.header.overrides/server" })
+ public void shouldProxyRequestWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${app}/request.authority.with.no.port/client",
diff --git a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/ConnectionManagementIT.java b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/ConnectionManagementIT.java
index cdf576e48a..0f8335a63b 100644
--- a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/ConnectionManagementIT.java
+++ b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/application/rfc7540/ConnectionManagementIT.java
@@ -59,6 +59,16 @@ public void shouldSendRequestWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${app}/http.get.exchange.with.route.header.overrides/client",
+ "${app}/http.get.exchange.with.route.header.overrides/server"
+ })
+ public void shouldSendRequestWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${app}/http.post.exchange/client",
diff --git a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/ConnectionManagementIT.java b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/ConnectionManagementIT.java
index b6ddc185b6..fbc8c34049 100644
--- a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/ConnectionManagementIT.java
+++ b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7230/ConnectionManagementIT.java
@@ -65,6 +65,15 @@ public void shouldSendRequestWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${net}/request.with.route.header.overrides/client",
+ "${net}/request.with.route.header.overrides/server" })
+ public void shouldSendRequestWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${net}response.with.connection.close/client",
diff --git a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/ConnectionManagementIT.java b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/ConnectionManagementIT.java
index 0e644a842a..a21a5e3967 100644
--- a/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/ConnectionManagementIT.java
+++ b/specs/binding-http.spec/src/test/java/io/aklivity/zilla/specs/binding/http/streams/network/rfc7540/ConnectionManagementIT.java
@@ -70,6 +70,17 @@ public void shouldSendRequestWithHeaderOverride() throws Exception
k3po.finish();
}
+ @Ignore
+ @Test
+ @Specification({
+ "${net}/http.get.exchange.with.route.header.overrides/client",
+ "${net}/http.get.exchange.with.route.header.overrides/server"
+ })
+ public void shouldSendRequestWithRouteHeaderOverrides() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${net}/http.unknown.authority/client",
diff --git a/specs/binding-kafka-grpc.spec/pom.xml b/specs/binding-kafka-grpc.spec/pom.xml
index f82ba50e5e..244544f375 100644
--- a/specs/binding-kafka-grpc.spec/pom.xml
+++ b/specs/binding-kafka-grpc.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-kafka.spec/pom.xml b/specs/binding-kafka.spec/pom.xml
index dfa3737154..0ff26f7688 100644
--- a/specs/binding-kafka.spec/pom.xml
+++ b/specs/binding-kafka.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-kafka.spec/src/main/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctions.java b/specs/binding-kafka.spec/src/main/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctions.java
index 3b63f9ed53..f719992155 100644
--- a/specs/binding-kafka.spec/src/main/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctions.java
+++ b/specs/binding-kafka.spec/src/main/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctions.java
@@ -36,7 +36,6 @@
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaAckMode;
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaCapabilities;
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaConditionFW;
-import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaConfigFW;
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaDeltaFW;
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaDeltaType;
import io.aklivity.zilla.specs.binding.kafka.internal.types.KafkaDeltaTypeFW;
@@ -655,7 +654,7 @@ public static final class KafkaMemberAssignmentsBuilder
private final MutableDirectBuffer writeBuffer = new UnsafeBuffer(new byte[1024 * 8]);
private final Array32FW.Builder memberAssignmentsRW =
- new Array32FW.Builder(new MemberAssignmentFW.Builder(), new MemberAssignmentFW());
+ new Array32FW.Builder<>(new MemberAssignmentFW.Builder(), new MemberAssignmentFW());
public KafkaMemberAssignmentsBuilder()
{
@@ -683,7 +682,7 @@ class KafkaMemberBuilder
private final MemberAssignmentFW.Builder assignmentRW = new MemberAssignmentFW.Builder();
private final MutableDirectBuffer topicAssignmentBuffer = new UnsafeBuffer(new byte[1024 * 8]);
private final Array32FW.Builder topicAssignmentsRW =
- new Array32FW.Builder(new TopicAssignmentFW.Builder(), new TopicAssignmentFW());
+ new Array32FW.Builder<>(new TopicAssignmentFW.Builder(), new TopicAssignmentFW());
KafkaMemberBuilder(
String memberId)
@@ -791,7 +790,7 @@ public static final class KafkaTopicAssignmentsBuilder
private final MutableDirectBuffer writeBuffer = new UnsafeBuffer(new byte[1024 * 8]);
private final Array32FW.Builder topicAssignments =
- new Array32FW.Builder(new TopicAssignmentFW.Builder(), new TopicAssignmentFW());
+ new Array32FW.Builder<>(new TopicAssignmentFW.Builder(), new TopicAssignmentFW());
public KafkaTopicAssignmentsBuilder()
{
@@ -2460,6 +2459,13 @@ public KafkaProduceDataExBuilder sequence(
return this;
}
+ public KafkaProduceDataExBuilder crc32c(
+ long crc32c)
+ {
+ produceDataExRW.crc32c(crc32c);
+ return this;
+ }
+
public KafkaProduceDataExBuilder ackMode(
String ackMode)
{
@@ -3586,6 +3592,7 @@ public final class KafkaProduceDataExMatcherBuilder
private Long timestamp;
private Long producerId;
private Short producerEpoch;
+ private Integer crc32c;
private Integer sequence;
private KafkaAckMode ackMode;
private KafkaKeyFW.Builder keyRW;
@@ -3609,6 +3616,13 @@ public KafkaProduceDataExMatcherBuilder timestamp(
return this;
}
+ public KafkaProduceDataExMatcherBuilder crc32c(
+ int crc32c)
+ {
+ this.crc32c = crc32c;
+ return this;
+ }
+
public KafkaProduceDataExMatcherBuilder producerId(
long producerId)
{
@@ -3701,7 +3715,10 @@ private boolean match(
final KafkaProduceDataExFW produceDataEx = dataEx.produce();
return matchDeferred(produceDataEx) &&
matchTimestamp(produceDataEx) &&
+ matchProducerId(produceDataEx) &&
+ matchProducerEpoch(produceDataEx) &&
matchSequence(produceDataEx) &&
+ matchCrc32c(produceDataEx) &&
matchAckMode(produceDataEx) &&
matchKey(produceDataEx) &&
matchHeaders(produceDataEx);
@@ -3731,6 +3748,12 @@ private boolean matchProducerEpoch(
return producerEpoch == null || producerEpoch == produceDataEx.producerEpoch();
}
+ private boolean matchCrc32c(
+ final KafkaProduceDataExFW produceDataEx)
+ {
+ return crc32c == null || crc32c == produceDataEx.crc32c();
+ }
+
private boolean matchSequence(
final KafkaProduceDataExFW produceDataEx)
{
@@ -3785,14 +3808,11 @@ public final class KafkaMergedFetchDataExMatcherBuilder
{
private Integer deferred;
private Long timestamp;
- private Long producerId;
- private Short producerEpoch;
private Long filters;
private KafkaOffsetFW.Builder partitionRW;
private Array32FW.Builder progressRW;
private KafkaDeltaFW.Builder deltaRW;
private KafkaKeyFW.Builder keyRW;
- private KafkaKeyFW.Builder hashKeyRW;
private Array32FW.Builder headersRW;
private KafkaMergedFetchDataExMatcherBuilder()
@@ -3813,20 +3833,6 @@ public KafkaMergedFetchDataExMatcherBuilder timestamp(
return this;
}
- public KafkaMergedFetchDataExMatcherBuilder producerId(
- long producerId)
- {
- this.producerId = producerId;
- return this;
- }
-
- public KafkaMergedFetchDataExMatcherBuilder producerEpoch(
- short producerEpoch)
- {
- this.producerEpoch = producerEpoch;
- return this;
- }
-
public KafkaMergedFetchDataExMatcherBuilder filters(
long filters)
{
@@ -4135,7 +4141,6 @@ public final class KafkaMergedProduceDataExMatcherBuilder
private Long timestamp;
private Long producerId;
private Short producerEpoch;
- private Long filters;
private KafkaOffsetFW.Builder partitionRW;
private Array32FW.Builder progressRW;
private KafkaDeltaFW.Builder deltaRW;
@@ -4175,13 +4180,6 @@ public KafkaMergedProduceDataExMatcherBuilder producerEpoch(
return this;
}
- public KafkaMergedProduceDataExMatcherBuilder filters(
- long filters)
- {
- this.filters = filters;
- return this;
- }
-
public KafkaMergedProduceDataExMatcherBuilder partition(
int partitionId,
long offset)
@@ -5540,7 +5538,6 @@ private boolean matchEvaluation(
public final class KafkaProduceBeginExMatcherBuilder
{
private String8FW transaction;
- private Long producerId;
private String16FW topic;
private KafkaOffsetFW.Builder partitionRW;
@@ -5555,13 +5552,6 @@ public KafkaProduceBeginExMatcherBuilder transaction(
return this;
}
- public KafkaProduceBeginExMatcherBuilder producerId(
- long producerId)
- {
- this.producerId = producerId;
- return this;
- }
-
public KafkaProduceBeginExMatcherBuilder topic(
String topic)
{
@@ -5833,7 +5823,6 @@ public final class KafkaMergedBeginExMatcherBuilder
private KafkaEvaluation evaluation;
private KafkaAckMode ackMode;
private Array32FW.Builder filtersRW;
- private Array32FW.Builder configsFW;
private KafkaMergedBeginExMatcherBuilder()
{
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/client.rpt
new file mode 100644
index 0000000000..14775554de
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/client.rpt
@@ -0,0 +1,84 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property deltaMillis 0L
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+
+read notify ROUTED_BROKER_CLIENT
+
+connect await ROUTED_BROKER_CLIENT
+ "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+ option zilla:affinity 0xb1
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(1724846895864)
+ .ackMode("LEADER_ONLY")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write zilla:data.empty
+write flush
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/server.rpt
new file mode 100644
index 0000000000..3f653d0d05
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.empty.crc/server.rpt
@@ -0,0 +1,80 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+write flush
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+write flush
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(1724846895864)
+ .ackMode("LEADER_ONLY")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read zilla:data.empty
+
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.header/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.header/client.rpt
index 56698e1436..2bb639d4c8 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.header/client.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.header/client.rpt
@@ -73,7 +73,7 @@ read zilla:begin.ext ${kafka:beginEx()
write zilla:data.ext ${kafka:dataEx()
.typeId(zilla:id("kafka"))
.produce()
- .timestamp(newTimestamp)
+ .timestamp(1716424650323)
.header("header1", "value1")
.build()
.build()}
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/client.rpt
new file mode 100644
index 0000000000..ef9750d660
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/client.rpt
@@ -0,0 +1,83 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property deltaMillis 0L
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+
+read notify ROUTED_BROKER_CLIENT
+
+connect await ROUTED_BROKER_CLIENT
+ "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+ option zilla:affinity 0xb1
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(1724846895864)
+ .ackMode("LEADER_ONLY")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write flush
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/server.rpt
new file mode 100644
index 0000000000..9f9d614f8a
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.null.crc/server.rpt
@@ -0,0 +1,80 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+write flush
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+write flush
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(1724846895864)
+ .ackMode("LEADER_ONLY")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read zilla:data.null
+
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.100k/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.100k/client.rpt
index cd5237ac4a..36eb3ab34c 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.100k/client.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.100k/client.rpt
@@ -74,7 +74,7 @@ write zilla:data.ext ${kafka:dataEx()
.typeId(zilla:id("kafka"))
.produce()
.deferred(102400 - 8192 + 512 + 512)
- .timestamp(newTimestamp)
+ .timestamp(1716424650323)
.build()
.build()}
write zilla:data.ext ${kafka:dataEx()
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/client.rpt
new file mode 100644
index 0000000000..1cd72dba27
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/client.rpt
@@ -0,0 +1,117 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property deltaMillis 0L
+property newTimestamp ${kafka:timestamp() + deltaMillis}
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+
+read notify ROUTED_BROKER_CLIENT
+
+connect await ROUTED_BROKER_CLIENT
+ "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+ option zilla:affinity 0xb1
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(0)
+ .build()
+ .build()}
+
+connected
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(0)
+ .build()
+ .build()}
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(6)
+ .timestamp(newTimestamp)
+ .sequence(0)
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(newTimestamp)
+ .sequence(0)
+ .build()
+ .build()}
+write " world"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(6)
+ .timestamp(newTimestamp)
+ .sequence(1)
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .timestamp(newTimestamp)
+ .sequence(1)
+ .build()
+ .build()}
+write " world"
+write flush
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/server.rpt
new file mode 100644
index 0000000000..51950be58e
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/message.value.repeated.fragmented/server.rpt
@@ -0,0 +1,82 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+write flush
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+write flush
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(0)
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(0)
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .build()
+ .build()}
+read "Hello, world"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .build()
+ .build()}
+read "Hello, world"
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/client.rpt
new file mode 100644
index 0000000000..94c2ee5b9e
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/client.rpt
@@ -0,0 +1,273 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property deltaMillis 0L
+
+connect "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+
+read notify ROUTED_BROKER_CLIENT
+
+connect await ROUTED_BROKER_CLIENT
+ "zilla://streams/app0"
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+ option zilla:affinity 0xb1
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
+
+
+write option zilla:flags "init"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .crc32c(1297420392)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write "Hello,"
+write flush
+
+write option zilla:flags "fin"
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+write " World!"
+write flush
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/server.rpt
new file mode 100644
index 0000000000..d0b35da7b5
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/application/produce/messages.fragmented.crc/server.rpt
@@ -0,0 +1,228 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property serverAddress "zilla://streams/app0"
+
+accept ${serverAddress}
+ option zilla:window 8192
+ option zilla:transmission "half-duplex"
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .topic("test")
+ .build()
+ .build()}
+write flush
+
+write zilla:data.ext ${kafka:dataEx()
+ .typeId(zilla:id("kafka"))
+ .meta()
+ .partition(0, 177)
+ .build()
+ .build()}
+write flush
+
+accepted
+
+read zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+connected
+
+write zilla:begin.ext ${kafka:beginEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .topic("test")
+ .partition(2)
+ .build()
+ .build()}
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .deferred(7)
+ .ackMode("NONE")
+ .key("key1")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read "Hello,"
+
+read zilla:data.ext ${kafka:matchDataEx()
+ .typeId(zilla:id("kafka"))
+ .produce()
+ .ackMode("IN_SYNC_REPLICAS")
+ .header("header1", "value1")
+ .build()
+ .build()}
+read " World!"
\ No newline at end of file
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/client.rpt
new file mode 100644
index 0000000000..95914de62e
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/client.rpt
@@ -0,0 +1,132 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkConnectWindow 8192
+
+property newRequestId ${kafka:newRequestId()}
+property produceWaitMax 500
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 26 # size
+ 3s # metadata
+ 5s # v5
+ ${newRequestId}
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+read 97 # size
+ ${newRequestId}
+ [0..4]
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+read notify ROUTED_BROKER_SERVER
+
+connect await ROUTED_BROKER_SERVER
+ "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+write zilla:begin.ext ${proxy:beginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+write 132 # size
+ 0s # produce
+ 3s # v3
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s # transactional id
+ 0s # acks
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2 # partition
+ 87 # record set size
+ 0L # first offset
+ 75 # length
+ -1
+ [0x02]
+ 1548293632
+ 0s
+ 0 # last offset delta
+ 1724846895864L # first timestamp
+ 1724846895864L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(25)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ [0x00]
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 44
+ ${newRequestId}
+ 1 # topics
+ 4s "test"
+ 1 # partitions
+ 2 # partition
+ 0s # no error
+ 20L # base offset
+ [0..8] # log append time
+ [0..4] # throttle ms
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/server.rpt
new file mode 100644
index 0000000000..b0c17dc1d9
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.empty.crc/server.rpt
@@ -0,0 +1,125 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 26 # size
+ 3s # metadata
+ 5s # v5
+ (int:requestId)
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+write 97 # size
+ ${requestId}
+ 0
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+accepted
+
+read zilla:begin.ext ${proxy:matchBeginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+read 132
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 87 # record set size
+ 0L # first offset
+ 75 # length
+ -1
+ [0x02]
+ 1548293632
+ 0s
+ 0 # last offset delta
+ 1724846895864L # first timestamp
+ 1724846895864L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(25)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ [0x00]
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes # value bytes
+
+write 44
+ ${requestId}
+ 1 # topics
+ 4s "test"
+ 1 # partitions
+ 2 # partition 0
+ 0s # no error
+ 20L # base offset
+ 0L # log append time
+ 0 # throttle
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/client.rpt
index 15f0f6a9f0..7e2183fa2e 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/client.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/client.rpt
@@ -98,11 +98,11 @@ write 140 # size
83 # length
-1
[0x02]
- 0x4e8723aa
+ -1568090010
0s
0 # last offset delta
- ${newTimestamp} # first timestamp
- ${newTimestamp} # last timestamp
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
-1L
-1s
-1
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/server.rpt
index 3738b7d640..ff11a9a208 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/server.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.header/server.rpt
@@ -94,11 +94,11 @@ read 140
83 # length
-1
[0x02]
- [0..4]
+ -1568090010
0s
0 # last offset delta
- (long:timestamp) # first timestamp
- ${timestamp} # last timestamp
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
-1L
-1s
-1
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/client.rpt
new file mode 100644
index 0000000000..4c2b9673b7
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/client.rpt
@@ -0,0 +1,132 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkConnectWindow 8192
+
+property newRequestId ${kafka:newRequestId()}
+property produceWaitMax 500
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 26 # size
+ 3s # metadata
+ 5s # v5
+ ${newRequestId}
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+read 97 # size
+ ${newRequestId}
+ [0..4]
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+read notify ROUTED_BROKER_SERVER
+
+connect await ROUTED_BROKER_SERVER
+ "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+write zilla:begin.ext ${proxy:beginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+write 132 # size
+ 0s # produce
+ 3s # v3
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s # transactional id
+ 0s # acks
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2 # partition
+ 87 # record set size
+ 0L # first offset
+ 75 # length
+ -1
+ [0x02]
+ 61721183
+ 0s
+ 0 # last offset delta
+ 1724846895864L # first timestamp
+ 1724846895864L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(25)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ [0x01]
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 44
+ ${newRequestId}
+ 1 # topics
+ 4s "test"
+ 1 # partitions
+ 2 # partition
+ 0s # no error
+ 20L # base offset
+ [0..8] # log append time
+ [0..4] # throttle ms
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/server.rpt
new file mode 100644
index 0000000000..3e2a328e61
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.null.crc/server.rpt
@@ -0,0 +1,125 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 26 # size
+ 3s # metadata
+ 5s # v5
+ (int:requestId)
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+write 97 # size
+ ${requestId}
+ 0
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+accepted
+
+read zilla:begin.ext ${proxy:matchBeginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+read 132
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 87 # record set size
+ 0L # first offset
+ 75 # length
+ -1
+ [0x02]
+ 61721183
+ 0s
+ 0 # last offset delta
+ 1724846895864L # first timestamp
+ 1724846895864L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(25)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ [0x01]
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 44
+ ${requestId}
+ 1 # topics
+ 4s "test"
+ 1 # partitions
+ 2 # partition 0
+ 0s # no error
+ 20L # base offset
+ 0L # log append time
+ 0 # throttle
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/client.rpt
index 02ceee163b..bf165dbe13 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/client.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/client.rpt
@@ -98,11 +98,11 @@ write 102517 # size
102460 # length
-1
[0x02]
- 0x4e8723aa
+ 1383945041
0s
0 # last offset delta
- ${newTimestamp} # first timestamp
- ${newTimestamp} # last timestamp
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
-1L
-1s
-1
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/server.rpt
index 87b17099da..09d75d64dd 100644
--- a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/server.rpt
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/message.value.100k/server.rpt
@@ -95,11 +95,11 @@ read 102517
102460 # length
-1
[0x02]
- [0..4]
+ 1383945041
0s
0 # last offset delta
- (long:timestamp) # first timestamp
- ${timestamp} # last timestamp
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
-1L
-1s
-1
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/client.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/client.rpt
new file mode 100644
index 0000000000..99b9edcd7f
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/client.rpt
@@ -0,0 +1,402 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkConnectWindow 8192
+
+property newRequestId ${kafka:newRequestId()}
+property produceWaitMax 500
+
+connect "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+connected
+
+write 26 # size
+ 3s # metadata
+ 5s # v5
+ ${newRequestId}
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+read 97 # size
+ ${newRequestId}
+ [0..4]
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+read notify ROUTED_BROKER_SERVER
+
+connect await ROUTED_BROKER_SERVER
+ "zilla://streams/net0"
+ option zilla:window ${networkConnectWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+write zilla:begin.ext ${proxy:beginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+write 145
+ 0s
+ 3s
+ ${newRequestId}
+ 5s "zilla" # client id
+ -1s
+ 0s
+ ${produceWaitMax}
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ 1716424650323L # first timestamp
+ 1716424650323L # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
diff --git a/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/server.rpt b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/server.rpt
new file mode 100644
index 0000000000..cb30acd937
--- /dev/null
+++ b/specs/binding-kafka.spec/src/main/scripts/io/aklivity/zilla/specs/binding/kafka/streams/network/produce.v3/messages.fragmented.crc/server.rpt
@@ -0,0 +1,395 @@
+#
+# Copyright 2021-2023 Aklivity Inc.
+#
+# Aklivity licenses this file to you 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:
+#
+# http://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.
+#
+
+property networkAcceptWindow 8192
+
+accept "zilla://streams/net0"
+ option zilla:window ${networkAcceptWindow}
+ option zilla:transmission "duplex"
+ option zilla:byteorder "network"
+
+accepted
+
+connected
+
+read 26 # size
+ 3s # metadata
+ 5s # v5
+ (int:requestId)
+ 5s "zilla" # client id
+ 1 # topics
+ 4s "test" # "test" topic
+ [0x00] # allow_auto_topic_creation
+
+write 97 # size
+ ${requestId}
+ 0
+ 1 # brokers
+ 0xb1 # broker id
+ 19s "broker1.example.com" # host name
+ 9092 # port
+ -1s # no rack
+ 9s "cluster 1" # cluster id
+ 1 # controller id
+ 1 # topics
+ 0s # no error
+ 4s "test" # "test" topic
+ [0x00] # not internal
+ 1 # partitions
+ 0s # no error
+ 0 # partition
+ 0xb1 # leader
+ 0 # no replicas
+ -1 # no in-sync replicas
+ 0 # offline replicas
+
+accepted
+
+read zilla:begin.ext ${proxy:matchBeginEx()
+ .typeId(zilla:id("proxy"))
+ .addressInet()
+ .protocol("stream")
+ .source("0.0.0.0")
+ .destination("broker1.example.com")
+ .sourcePort(0)
+ .destinationPort(9092)
+ .build()
+ .info()
+ .authority("broker1.example.com")
+ .build()
+ .build()}
+
+connected
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
+
+read 145
+ 0s
+ 3s
+ (int:requestId)
+ 5s "zilla" # client id
+ -1s
+ [0..2]
+ [0..4]
+ 1
+ 4s "test"
+ 1
+ 2
+ 100 # record set size
+ 0L # first offset
+ 88 # length
+ -1
+ [0x02]
+ 1012046046
+ 8s
+ 0 # last offset delta
+ (long:timestamp) # first timestamp
+ ${timestamp} # last timestamp
+ -1L
+ -1s
+ -1
+ 1 # records
+ ${kafka:varint(38)}
+ [0x00]
+ ${kafka:varint(0)}
+ ${kafka:varint(0)}
+ ${kafka:varint(4)} # key
+ "key1"
+ ${kafka:varint(13)}
+ "Hello, World!"
+ ${kafka:varint(1)} # headers
+ ${kafka:varint(7)} # key size
+ "header1" # key bytes
+ ${kafka:varint(6)} # value size
+ "value1" # value bytes
diff --git a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctionsTest.java b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctionsTest.java
index 90b6675b93..f55fb3f26f 100644
--- a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctionsTest.java
+++ b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/internal/KafkaFunctionsTest.java
@@ -1223,9 +1223,6 @@ public void shouldMatchMergedFetchDataExtensionWithLatestOffset() throws Excepti
.partition(0, 0L, 1L)
.progress(0, 1L, 1L)
.timestamp(12345678L)
- .producerId(8L)
- .producerEpoch((short) 2)
- .timestamp(12345678L)
.key("match")
.header("name", "value")
.build()
@@ -2790,29 +2787,6 @@ public void shouldMatchProduceBeginExtensionTransaction() throws Exception
assertNotNull(matcher.match(byteBuf));
}
- @Test
- public void shouldMatchProduceBeginExtensionProducerId() throws Exception
- {
- BytesMatcher matcher = KafkaFunctions.matchBeginEx()
- .produce()
- .producerId(1L)
- .build()
- .build();
-
- ByteBuffer byteBuf = ByteBuffer.allocate(1024);
-
- new KafkaBeginExFW.Builder()
- .wrap(new UnsafeBuffer(byteBuf), 0, byteBuf.capacity())
- .typeId(0x01)
- .produce(f -> f
- .transaction("transaction")
- .topic("test")
- .partition(p -> p.partitionId(0).partitionOffset(0L)))
- .build();
-
- assertNotNull(matcher.match(byteBuf));
- }
-
@Test
public void shouldMatchProduceBeginExtensionTopic() throws Exception
{
@@ -3685,6 +3659,8 @@ public void shouldMatchProduceDataExtensionTimestamp() throws Exception
new KafkaDataExFW.Builder().wrap(new UnsafeBuffer(byteBuf), 0, byteBuf.capacity())
.typeId(0x01)
.produce(p -> p.timestamp(12345678L)
+ .producerId(8L)
+ .producerEpoch((short) 1)
.sequence(0)
.key(k -> k.length(5)
.value(v -> v.set("match".getBytes(UTF_8))))
diff --git a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/application/ProduceIT.java b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/application/ProduceIT.java
index 7a29a25aa0..90c3a5103c 100644
--- a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/application/ProduceIT.java
+++ b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/application/ProduceIT.java
@@ -146,6 +146,33 @@ public void shouldSendMessageValue() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${app}/messages.fragmented.crc/client",
+ "${app}/messages.fragmented.crc/server"})
+ public void shouldSendFragmentedMessagesWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/message.empty.crc/client",
+ "${app}/message.empty.crc/server"})
+ public void shouldSendMessageEmptyWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${app}/message.null.crc/client",
+ "${app}/message.null.crc/server"})
+ public void shouldSendMessageNullWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${app}/message.producer.id/client",
@@ -257,6 +284,15 @@ public void shouldSendMessageValueRepeated() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${app}/message.value.repeated.fragmented/client",
+ "${app}/message.value.repeated.fragmented/server"})
+ public void shouldSendMessageValueRepeatedWhenFragmented() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${app}/message.values.sequential/client",
diff --git a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/network/ProduceIT.java b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/network/ProduceIT.java
index 112cdd4a07..6b0589c2ee 100644
--- a/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/network/ProduceIT.java
+++ b/specs/binding-kafka.spec/src/test/java/io/aklivity/zilla/specs/binding/kafka/streams/network/ProduceIT.java
@@ -119,6 +119,33 @@ public void shouldSendMessageValue() throws Exception
k3po.finish();
}
+ @Test
+ @Specification({
+ "${net}/messages.fragmented.crc/client",
+ "${net}/messages.fragmented.crc/server"})
+ public void shouldSendFragmentedMessagesWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/message.empty.crc/client",
+ "${net}/message.empty.crc/server"})
+ public void shouldSendMessageEmptyWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
+ @Test
+ @Specification({
+ "${net}/message.null.crc/client",
+ "${net}/message.null.crc/server"})
+ public void shouldSendMessageNullWithCrc() throws Exception
+ {
+ k3po.finish();
+ }
+
@Test
@Specification({
"${net}/message.producer.id/client",
diff --git a/specs/binding-mqtt-kafka.spec/pom.xml b/specs/binding-mqtt-kafka.spec/pom.xml
index e24320d21d..e064f12793 100644
--- a/specs/binding-mqtt-kafka.spec/pom.xml
+++ b/specs/binding-mqtt-kafka.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-mqtt.spec/pom.xml b/specs/binding-mqtt.spec/pom.xml
index d95395046e..161d11ab7d 100644
--- a/specs/binding-mqtt.spec/pom.xml
+++ b/specs/binding-mqtt.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-openapi-asyncapi.spec/pom.xml b/specs/binding-openapi-asyncapi.spec/pom.xml
index 4895525fdb..7e7301a5a4 100644
--- a/specs/binding-openapi-asyncapi.spec/pom.xml
+++ b/specs/binding-openapi-asyncapi.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-openapi.spec/pom.xml b/specs/binding-openapi.spec/pom.xml
index 034bff4c8b..fd7414c0f5 100644
--- a/specs/binding-openapi.spec/pom.xml
+++ b/specs/binding-openapi.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-proxy.spec/pom.xml b/specs/binding-proxy.spec/pom.xml
index f1b122d95f..23be10b63f 100644
--- a/specs/binding-proxy.spec/pom.xml
+++ b/specs/binding-proxy.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-sse-kafka.spec/pom.xml b/specs/binding-sse-kafka.spec/pom.xml
index 8a727b7284..63fe7fe020 100644
--- a/specs/binding-sse-kafka.spec/pom.xml
+++ b/specs/binding-sse-kafka.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-sse.spec/pom.xml b/specs/binding-sse.spec/pom.xml
index 640e4f26c4..591f1c2736 100644
--- a/specs/binding-sse.spec/pom.xml
+++ b/specs/binding-sse.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-tcp.spec/pom.xml b/specs/binding-tcp.spec/pom.xml
index 228e793acb..826644606d 100644
--- a/specs/binding-tcp.spec/pom.xml
+++ b/specs/binding-tcp.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/binding-tls.spec/pom.xml b/specs/binding-tls.spec/pom.xml
index 7cff954f29..81e8ea5e8a 100644
--- a/specs/binding-tls.spec/pom.xml
+++ b/specs/binding-tls.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
@@ -102,6 +102,7 @@
**/signers
**/*.csr
**/*.crt
+ **/*.pem
diff --git a/specs/binding-tls.spec/src/main/resources/META-INF/zilla/tls.idl b/specs/binding-tls.spec/src/main/resources/META-INF/zilla/tls.idl
index 0f08dac473..baa53655f6 100644
--- a/specs/binding-tls.spec/src/main/resources/META-INF/zilla/tls.idl
+++ b/specs/binding-tls.spec/src/main/resources/META-INF/zilla/tls.idl
@@ -23,20 +23,7 @@ scope tls
TLS_PROTOCOL_REJECTED (2),
TLS_KEY_REJECTED (3),
TLS_PEER_NOT_VERIFIED (4),
- TLS_HANDSHAKE_FAILED (5),
- TLS_KEY_VERIFICATION_FAILED (6)
- }
-
- enum TlsKeyFailureType (uint8)
- {
- TLS_KEY_MISSING (1),
- TLS_KEY_INVALID (2)
- }
-
- struct TlsKeyVerificationFailedEx extends core::stream::Extension
- {
- TlsKeyFailureType failureType;
- string8 keyName;
+ TLS_HANDSHAKE_FAILED (5)
}
union TlsEventEx switch (TlsEventType)
@@ -46,7 +33,6 @@ scope tls
case TLS_KEY_REJECTED: core::stream::Extension tlsKeyRejected;
case TLS_PEER_NOT_VERIFIED: core::stream::Extension tlsPeerNotVerified;
case TLS_HANDSHAKE_FAILED: core::stream::Extension tlsHandshakeFailed;
- case TLS_KEY_VERIFICATION_FAILED: TlsKeyVerificationFailedEx tlsKeyVerificationFailed;
}
}
}
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.2.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.2.yaml
index 2ba1cbe646..ca4f7df465 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.2.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.2.yaml
@@ -18,16 +18,106 @@
name: test
vaults:
bridge:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.3.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.3.yaml
index 64dd7dded0..393b614b80 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.3.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/bridge.tls1.3.yaml
@@ -17,17 +17,107 @@
---
name: test
vaults:
- bridge:
- type: filesystem
- options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ bridge:
+ type: test
+ options:
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.default.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.default.yaml
index 8992afeab2..7a1f56f310 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.default.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.default.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.yaml
index b2adf119eb..bb8b80d637 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.alpn.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.event.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.event.yaml
index 1049afc790..1f12bde1da 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.event.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.event.yaml
@@ -28,12 +28,32 @@ telemetry:
message: The client and server could not negotiate the desired level of security.
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.signer.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.signer.yaml
index 698b8b6794..3bd2a577f4 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.signer.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.signer.yaml
@@ -18,20 +18,128 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
- signers:
- store: stores/server/trust
- type: pkcs12
- password: generated
- keys:
- store: stores/client/keys
- type: pkcs12
- password: generated
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ signer:
+ alias: clientca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+ y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+ tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+ LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+ J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+ KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+ KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+ /WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+ uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+ epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+ p0XXN2RYSD7FW7RNb4xcNQ==
+ -----END CERTIFICATE-----
+ key:
+ alias: client1
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrn6NhWOvX69jT
+ 5Xv/MqGEMLtCtMbR3W+ev4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeX
+ K3z/4UDULKa0sAJBl7/YdffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK
+ 1P4no9nhjrQLW3u4j+7d2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR
+ 7/NYvpF7fM01yVEyUnvPsOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc
+ 7322Vocom8S1JXuoVEhxBtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3k
+ vve7WsZjAgMBAAECggEARW+B93m1eZdnE1vuTfKbHFNm//cJ1ZDEfzK4fT1lFXSw
+ mm6T22aSAP63qZzODcLIZ0icAjnT4xxgX83tdc0ZoH4sOEBZuVX+/UHFN2UewDew
+ uwz/dq/459fhly0O7EHKdqomCEmmSYYje5Nu/HSzSymkOFAb/zDkkIa+BIDz4Whv
+ tM8h0SOPCMcW78E2fEfLC5sEATK0B0yJ2QHCJAJ1E1DuiNZGQKn0Jk56QQxdmpsM
+ a8hOY216RcCq0bhh5+WRCd/fKgmMj9ExgG7JL+JshW69UArIPYfR5NmlLEUL93Ua
+ rj2yK1U27YI+m0Vhsn+caOZIxAflmBLUG1iny2ANoQKBgQDf5CxqFvvj9naqVy1v
+ 6GgUFbb0MLsdvbtqbmQq30gtSJqe+5ct2kIHRoWmb/ZxgW+SCp4qFFSWVPysMOtK
+ qHQHX8N4vwqHDXcgnX5rvaEWlSRziJe8VSyfUPtvDWYua7FbXMV0rvyPerAAzOff
+ 9sykNcrXE8MsQn/9u9tXTW7aEwKBgQDEPIkXjyUGjZVwo0BjQnZY1qMqX9n07Nk/
+ kA+3T23Jvd2sVRYtM8d0y0KmrBUueZmxQpbbdWnD5jeoA8kdJVybO3WOux5O39Sn
+ e6CsLtaDeNrI0d+0hxGdSCLavavUn0QhUyJS7W6kUhP+1jvghcBstg5xglK/kuij
+ JNEsvnHscQKBgGHKVr/r1A+o6gHS3AzCFRuYtHWKB2ChK4f8OZIkPnFRAgAo4cps
+ R2TIH74nrxu6rlG6g4TbpmqXKlnHcC4Gz88cu9M0llOQxRSg1cQXapHjiK1R3vyq
+ szfO9lN2Jchmz2uZZMFnYPcGXnOrDChdstqSiEtS3W+qiB76e35xWtazAoGBAJku
+ f/1p+gcRgnP4m4xgFy3l7lxnkiYEtNlPRVVcpWDz2xRpHvSOFMZw3Ehqz+YFehnK
+ 1yPclYEcNpnqypT70zxBv9R48IG1a7SJZrTBoyXhszdLzQqI3REffEWwBW/mGzLA
+ SGzfdpzCjWVCKl9rSPgTbVbh2mI89WhUlwwMZbWhAoGANs9+Ahxk/PEh8qJF5Oie
+ M6Yn+tt4Zc/XiNG1bllLX0aYHmN7TsLqvd4Bz6w44SsK1X2AUjRqutzO9/e8lIHL
+ YjOdLlotdORDWQc6vGC23TQmiszGoszdtQQDhCkocVi3jV0r8ocUDtOPkijnfl39
+ pynEfemPcP7gg/7Gh7mD998=
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDQDCCAiigAwIBAgIEQSJ/bDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM2
+ MDBaFw0yNjExMTgwMDM2MDBaMBIxEDAOBgNVBAMTB2NsaWVudDEwggEiMA0GCSqG
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrn6NhWOvX69jT5Xv/MqGEMLtCtMbR3W+e
+ v4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeXK3z/4UDULKa0sAJBl7/Y
+ dffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK1P4no9nhjrQLW3u4j+7d
+ 2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR7/NYvpF7fM01yVEyUnvP
+ sOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc7322Vocom8S1JXuoVEhx
+ BtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3kvve7WsZjAgMBAAGjUjBQ
+ MB0GA1UdDgQWBBRy6TrHDFXQtgfFDIbeMKcN6zR93TAOBgNVHQ8BAf8EBAMCBaAw
+ HwYDVR0jBBgwFoAU5obnoO20A2wTKYH+uCuF4ZW6cCswDQYJKoZIhvcNAQELBQAD
+ ggEBAE3597yVknsz60h4WJGqvARtSaKIL3TDolOVbRG8/mLWGTCa3CguVMLVLE1I
+ xtnQZVP4Jgaacks7VmZodKNbTkhdsp2SYLtwjVETFn37KmlStO+th2E9x+RCembL
+ j5q98OzeQzQgiw6f0AnJIilSXW+JDGevTolnUeZNsqBJ5KiZskRDmK+ghQNR1gJ8
+ VctMpMJEGl9zM6g+xgwrdqL9RXb6DpArqR2N+EtTp/Tpv3mkunkkficOlMhTYyGi
+ SXEE6xozLciQk8+EOF9i8leynKxzhera9nDkGeqEHM+5kEoBMGZiJDjLfsT71jfW
+ QEcYtMb3OsuhRL2yiXMJXwSYsFU=
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+ y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+ tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+ LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+ J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+ KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+ KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+ /WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+ uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+ epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+ p0XXN2RYSD7FW7RNb4xcNQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.yaml
index ce7fc387c5..bdecbc445d 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.mutual.yaml
@@ -18,16 +18,104 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
- keys:
- store: stores/client/keys
- type: pkcs12
- password: generated
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ key:
+ alias: client1
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrn6NhWOvX69jT
+ 5Xv/MqGEMLtCtMbR3W+ev4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeX
+ K3z/4UDULKa0sAJBl7/YdffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK
+ 1P4no9nhjrQLW3u4j+7d2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR
+ 7/NYvpF7fM01yVEyUnvPsOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc
+ 7322Vocom8S1JXuoVEhxBtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3k
+ vve7WsZjAgMBAAECggEARW+B93m1eZdnE1vuTfKbHFNm//cJ1ZDEfzK4fT1lFXSw
+ mm6T22aSAP63qZzODcLIZ0icAjnT4xxgX83tdc0ZoH4sOEBZuVX+/UHFN2UewDew
+ uwz/dq/459fhly0O7EHKdqomCEmmSYYje5Nu/HSzSymkOFAb/zDkkIa+BIDz4Whv
+ tM8h0SOPCMcW78E2fEfLC5sEATK0B0yJ2QHCJAJ1E1DuiNZGQKn0Jk56QQxdmpsM
+ a8hOY216RcCq0bhh5+WRCd/fKgmMj9ExgG7JL+JshW69UArIPYfR5NmlLEUL93Ua
+ rj2yK1U27YI+m0Vhsn+caOZIxAflmBLUG1iny2ANoQKBgQDf5CxqFvvj9naqVy1v
+ 6GgUFbb0MLsdvbtqbmQq30gtSJqe+5ct2kIHRoWmb/ZxgW+SCp4qFFSWVPysMOtK
+ qHQHX8N4vwqHDXcgnX5rvaEWlSRziJe8VSyfUPtvDWYua7FbXMV0rvyPerAAzOff
+ 9sykNcrXE8MsQn/9u9tXTW7aEwKBgQDEPIkXjyUGjZVwo0BjQnZY1qMqX9n07Nk/
+ kA+3T23Jvd2sVRYtM8d0y0KmrBUueZmxQpbbdWnD5jeoA8kdJVybO3WOux5O39Sn
+ e6CsLtaDeNrI0d+0hxGdSCLavavUn0QhUyJS7W6kUhP+1jvghcBstg5xglK/kuij
+ JNEsvnHscQKBgGHKVr/r1A+o6gHS3AzCFRuYtHWKB2ChK4f8OZIkPnFRAgAo4cps
+ R2TIH74nrxu6rlG6g4TbpmqXKlnHcC4Gz88cu9M0llOQxRSg1cQXapHjiK1R3vyq
+ szfO9lN2Jchmz2uZZMFnYPcGXnOrDChdstqSiEtS3W+qiB76e35xWtazAoGBAJku
+ f/1p+gcRgnP4m4xgFy3l7lxnkiYEtNlPRVVcpWDz2xRpHvSOFMZw3Ehqz+YFehnK
+ 1yPclYEcNpnqypT70zxBv9R48IG1a7SJZrTBoyXhszdLzQqI3REffEWwBW/mGzLA
+ SGzfdpzCjWVCKl9rSPgTbVbh2mI89WhUlwwMZbWhAoGANs9+Ahxk/PEh8qJF5Oie
+ M6Yn+tt4Zc/XiNG1bllLX0aYHmN7TsLqvd4Bz6w44SsK1X2AUjRqutzO9/e8lIHL
+ YjOdLlotdORDWQc6vGC23TQmiszGoszdtQQDhCkocVi3jV0r8ocUDtOPkijnfl39
+ pynEfemPcP7gg/7Gh7mD998=
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDQDCCAiigAwIBAgIEQSJ/bDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM2
+ MDBaFw0yNjExMTgwMDM2MDBaMBIxEDAOBgNVBAMTB2NsaWVudDEwggEiMA0GCSqG
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrn6NhWOvX69jT5Xv/MqGEMLtCtMbR3W+e
+ v4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeXK3z/4UDULKa0sAJBl7/Y
+ dffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK1P4no9nhjrQLW3u4j+7d
+ 2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR7/NYvpF7fM01yVEyUnvP
+ sOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc7322Vocom8S1JXuoVEhx
+ BtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3kvve7WsZjAgMBAAGjUjBQ
+ MB0GA1UdDgQWBBRy6TrHDFXQtgfFDIbeMKcN6zR93TAOBgNVHQ8BAf8EBAMCBaAw
+ HwYDVR0jBBgwFoAU5obnoO20A2wTKYH+uCuF4ZW6cCswDQYJKoZIhvcNAQELBQAD
+ ggEBAE3597yVknsz60h4WJGqvARtSaKIL3TDolOVbRG8/mLWGTCa3CguVMLVLE1I
+ xtnQZVP4Jgaacks7VmZodKNbTkhdsp2SYLtwjVETFn37KmlStO+th2E9x+RCembL
+ j5q98OzeQzQgiw6f0AnJIilSXW+JDGevTolnUeZNsqBJ5KiZskRDmK+ghQNR1gJ8
+ VctMpMJEGl9zM6g+xgwrdqL9RXb6DpArqR2N+EtTp/Tpv3mkunkkficOlMhTYyGi
+ SXEE6xozLciQk8+EOF9i8leynKxzhera9nDkGeqEHM+5kEoBMGZiJDjLfsT71jfW
+ QEcYtMb3OsuhRL2yiXMJXwSYsFU=
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+ y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+ tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+ LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+ J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+ KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+ KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+ /WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+ uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+ epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+ p0XXN2RYSD7FW7RNb4xcNQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.ports.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.ports.yaml
index 0b3b17776b..4f9e375946 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.ports.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.ports.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.sni.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.sni.yaml
index 88269b559f..09090d5aab 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.sni.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.sni.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.yaml
index a09ccc16d8..adbc32c90c 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/client.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
client:
- type: filesystem
+ type: test
options:
- trust:
- store: stores/client/trust
- type: pkcs12
- password: generated
+ trust:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
app0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.default.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.default.yaml
index 05e079f4de..0cd74032f6 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.default.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.default.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.yaml
index 6a44f1e199..de228f2841 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.alpn.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.handshake.failed.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.handshake.failed.yaml
index d0dc007984..90f9162db6 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.handshake.failed.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.handshake.failed.yaml
@@ -28,12 +28,82 @@ telemetry:
message: The client and server could not negotiate the desired level of security.
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.tls.failed.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.tls.failed.yaml
index 2f5a3b47a4..9be3e9d57f 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.tls.failed.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.event.tls.failed.yaml
@@ -28,12 +28,82 @@ telemetry:
message: There was a generic error detected by an SSL subsystem.
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.keys.not.found.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.keys.not.found.yaml
index 16f38c838f..ae7ce716a2 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.keys.not.found.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.keys.not.found.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.requested.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.requested.yaml
index a4af0daacd..360d322148 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.requested.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.requested.yaml
@@ -18,16 +18,106 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
- trust:
- store: stores/server/trust
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ trust:
+ alias: clientca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+ y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+ tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+ LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+ J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+ KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+ KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+ /WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+ uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+ epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+ p0XXN2RYSD7FW7RNb4xcNQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.yaml
index d4db55f3ef..c7b2bcdbf4 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.mutual.yaml
@@ -18,16 +18,106 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
- trust:
- store: stores/server/trust
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
+ trust:
+ alias: clientca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+ y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+ tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+ LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+ J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+ KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+ KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+ /WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+ uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+ epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+ p0XXN2RYSD7FW7RNb4xcNQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.ports.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.ports.yaml
index 90fbf5a6d5..146693ea31 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.ports.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.ports.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.signer.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.signer.yaml
index e78e195a12..90e1ce87cb 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.signer.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.signer.yaml
@@ -18,12 +18,32 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- signers:
- store: stores/server/signers
- type: pkcs12
- password: generated
+ signer:
+ alias: serverca
+ entry: |
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.sni.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.sni.yaml
index 0307882adf..8721545d91 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.sni.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.sni.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.yaml b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.yaml
index f9734e6590..1d5c1227b2 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.yaml
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/server.yaml
@@ -18,12 +18,82 @@
name: test
vaults:
server:
- type: filesystem
+ type: test
options:
- keys:
- store: stores/server/keys
- type: pkcs12
- password: generated
+ key:
+ alias: localhost
+ entry: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+ jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+ woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+ JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+ tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+ qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+ jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+ 80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+ QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+ fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+ 8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+ 6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+ AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+ tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+ F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+ 8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+ 3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+ XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+ atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+ X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+ 5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
+ +6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+ 19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+ aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+ gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+ 4dIOj5XwbSpgiTBrWlctf87U
+ -----END PRIVATE KEY-----
+ -----BEGIN CERTIFICATE-----
+ MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+ CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+ n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+ RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+ u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+ VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+ 0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+ MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+ dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+ AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+ oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+ 6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+ prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+ 4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+ zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+ -----END CERTIFICATE-----
+ -----BEGIN CERTIFICATE-----
+ MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+ MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+ NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+ EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+ zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+ rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+ g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+ o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+ lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+ rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+ a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+ kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+ FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+ gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+ P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+ 5OQUWOWJqvzyJ8VIbBRDsQ==
+ -----END CERTIFICATE-----
bindings:
net0:
type: tls
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/keys.client1.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/keys.client1.pem
new file mode 100644
index 0000000000..df529b9b9f
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/keys.client1.pem
@@ -0,0 +1,69 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrn6NhWOvX69jT
+5Xv/MqGEMLtCtMbR3W+ev4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeX
+K3z/4UDULKa0sAJBl7/YdffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK
+1P4no9nhjrQLW3u4j+7d2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR
+7/NYvpF7fM01yVEyUnvPsOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc
+7322Vocom8S1JXuoVEhxBtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3k
+vve7WsZjAgMBAAECggEARW+B93m1eZdnE1vuTfKbHFNm//cJ1ZDEfzK4fT1lFXSw
+mm6T22aSAP63qZzODcLIZ0icAjnT4xxgX83tdc0ZoH4sOEBZuVX+/UHFN2UewDew
+uwz/dq/459fhly0O7EHKdqomCEmmSYYje5Nu/HSzSymkOFAb/zDkkIa+BIDz4Whv
+tM8h0SOPCMcW78E2fEfLC5sEATK0B0yJ2QHCJAJ1E1DuiNZGQKn0Jk56QQxdmpsM
+a8hOY216RcCq0bhh5+WRCd/fKgmMj9ExgG7JL+JshW69UArIPYfR5NmlLEUL93Ua
+rj2yK1U27YI+m0Vhsn+caOZIxAflmBLUG1iny2ANoQKBgQDf5CxqFvvj9naqVy1v
+6GgUFbb0MLsdvbtqbmQq30gtSJqe+5ct2kIHRoWmb/ZxgW+SCp4qFFSWVPysMOtK
+qHQHX8N4vwqHDXcgnX5rvaEWlSRziJe8VSyfUPtvDWYua7FbXMV0rvyPerAAzOff
+9sykNcrXE8MsQn/9u9tXTW7aEwKBgQDEPIkXjyUGjZVwo0BjQnZY1qMqX9n07Nk/
+kA+3T23Jvd2sVRYtM8d0y0KmrBUueZmxQpbbdWnD5jeoA8kdJVybO3WOux5O39Sn
+e6CsLtaDeNrI0d+0hxGdSCLavavUn0QhUyJS7W6kUhP+1jvghcBstg5xglK/kuij
+JNEsvnHscQKBgGHKVr/r1A+o6gHS3AzCFRuYtHWKB2ChK4f8OZIkPnFRAgAo4cps
+R2TIH74nrxu6rlG6g4TbpmqXKlnHcC4Gz88cu9M0llOQxRSg1cQXapHjiK1R3vyq
+szfO9lN2Jchmz2uZZMFnYPcGXnOrDChdstqSiEtS3W+qiB76e35xWtazAoGBAJku
+f/1p+gcRgnP4m4xgFy3l7lxnkiYEtNlPRVVcpWDz2xRpHvSOFMZw3Ehqz+YFehnK
+1yPclYEcNpnqypT70zxBv9R48IG1a7SJZrTBoyXhszdLzQqI3REffEWwBW/mGzLA
+SGzfdpzCjWVCKl9rSPgTbVbh2mI89WhUlwwMZbWhAoGANs9+Ahxk/PEh8qJF5Oie
+M6Yn+tt4Zc/XiNG1bllLX0aYHmN7TsLqvd4Bz6w44SsK1X2AUjRqutzO9/e8lIHL
+YjOdLlotdORDWQc6vGC23TQmiszGoszdtQQDhCkocVi3jV0r8ocUDtOPkijnfl39
+pynEfemPcP7gg/7Gh7mD998=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIEQSJ/bDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM2
+MDBaFw0yNjExMTgwMDM2MDBaMBIxEDAOBgNVBAMTB2NsaWVudDEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrn6NhWOvX69jT5Xv/MqGEMLtCtMbR3W+e
+v4f4QXGx3smk3nilx2qdcKGCs/vQsvIbcvRHACpMLgeXK3z/4UDULKa0sAJBl7/Y
+dffhvqguozJMsi09aNHHuC5pZjeu1BFEcxfhfMamyHcK1P4no9nhjrQLW3u4j+7d
+2AiuoMvvP6BznTr6Q95YHl6l5FxNCk9pevO1bDI6b0qR7/NYvpF7fM01yVEyUnvP
+sOJBChOn5PXvZbkAQZ4laaYcu8c55YmVxsrrstHdoiXc7322Vocom8S1JXuoVEhx
+BtO49bmrm5J7Si1JhAcTXS89r4uJr0g/tVg/5JmuAF3kvve7WsZjAgMBAAGjUjBQ
+MB0GA1UdDgQWBBRy6TrHDFXQtgfFDIbeMKcN6zR93TAOBgNVHQ8BAf8EBAMCBaAw
+HwYDVR0jBBgwFoAU5obnoO20A2wTKYH+uCuF4ZW6cCswDQYJKoZIhvcNAQELBQAD
+ggEBAE3597yVknsz60h4WJGqvARtSaKIL3TDolOVbRG8/mLWGTCa3CguVMLVLE1I
+xtnQZVP4Jgaacks7VmZodKNbTkhdsp2SYLtwjVETFn37KmlStO+th2E9x+RCembL
+j5q98OzeQzQgiw6f0AnJIilSXW+JDGevTolnUeZNsqBJ5KiZskRDmK+ghQNR1gJ8
+VctMpMJEGl9zM6g+xgwrdqL9RXb6DpArqR2N+EtTp/Tpv3mkunkkficOlMhTYyGi
+SXEE6xozLciQk8+EOF9i8leynKxzhera9nDkGeqEHM+5kEoBMGZiJDjLfsT71jfW
+QEcYtMb3OsuhRL2yiXMJXwSYsFU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+/WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+p0XXN2RYSD7FW7RNb4xcNQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/signers.clientca.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/signers.clientca.pem
new file mode 100644
index 0000000000..8cac297081
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/signers.clientca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+/WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+p0XXN2RYSD7FW7RNb4xcNQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/trust.serverca.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/trust.serverca.pem
new file mode 100644
index 0000000000..389ef06999
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/client/trust.serverca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+5OQUWOWJqvzyJ8VIbBRDsQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/keys.localhost.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/keys.localhost.pem
new file mode 100644
index 0000000000..606d0adb0f
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/keys.localhost.pem
@@ -0,0 +1,71 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEMgOIcsjICH10
+jYmaFQrVkoCcc4SNki6XZTHl3eeedv+rOJmoRKkuBNc2+HSfqiDtfrM9r/4PlVcw
+woA25NaAhuF7a59nn59GpdwSf6VH+/UPqgrF5ZBt7Kew3FFFg0Q/c5JxwC78xslP
+JwDdR9tqJDV5lrljou/XXum6H0W3to8J7r2faP9lHPCyPTu7ddGMFJpZnm2kV6av
+tfiqdsKSg1ysXBCLoYaGZXuIe9b3BuAHKWXbkXrRWpN0DqFVRTbVRirLm+GRHPTj
+qCO1d3vkfpGPe0sVigrH3sH8Z4+6xQiDLV8hYN4PfFK6rs/QDgBcLRXgoIxNGj4/
+jTBeWJsNAgMBAAECggEALkIMizNlG6TEz6I/e1DSySBNqpWh/y8kRRXa+fOkFLzB
+80DZGc92hB8oDxHtjWezc8uXv0erg0kW0axKRqcV3B/xMgRiBTChgSrCBKqL8bj6
+QsM43wgNUZ2mvBB2KQmWIc/CC4fgjEDiXgM4NPQoS5JV+WEMPO3B1unN+dA9w96h
+fn/LsuZddxNA6HxwFwLVu40RCA88447Y2BnK8WI5Li8LurlWzCHd94/c5XBlk6bR
+8i5KTF8m2mwcA/juTAzDXPc/GxpHnQ+tPofEplnrMf3Pagxze+VBtcDyb2pftfU6
+6U1yYj/DYCEy5D3GftBJaUdGMy3HJKBqytUiXsMxEQKBgQDpXHi45410AHGMCWgA
+AeXV2NhL9mPUZp8ayp46ccwU5/Z2+rMA8tqUeCEEygewwH943QmmtgnrRVQkSfVr
+tzGrScekMXRWkegGz2hDAjpYcOYAhaxFs8+lZh1C90KMhplSScA0WgUG6v0/GcYP
+F9taSMhoSPdVgWT3wpHzu7m8QwKBgQDXOoclWDI4InRBzlG+neEmvGaTnu74F9Gn
+8cXZeMvnP3RpeaUPPbYk/lUXB2UwSQL4NmFHjUiu/7/RCyKEEIotesBDoXzJgAwA
+3Pa6wj1PyZjw3d/DCj1ie32iASetgvMrWOYMXbBwoAN/9KnAT+jSDYzYoIqgDvqr
+XPltek1+bwKBgQDK8fufOujewF//XFLQ5tWd85awAVhyaMjnwNx4kWzdpZnHgWFZ
+atrDEfINOzIS+mYhk4GINXpM3J8/a/5+rfQJNgHkfonhm8SA37GZzP822QB95cKb
+X7Qu8K50BZsfoRMH6FG6das3h/ez/9MqXyIJ05BOeV54Qy6u9cx+fn0aBwKBgD8y
+5HRGzZhGJbUdDEvYuEBTmhtG1AztkZarG9mb6uPgciJ2oc0z5cQ/GmVEuqXPCpwv
++6izB+C7IGwVxPDamnh4TvSM+sOAYgXu3FRCrRY8sDgqLoZq/1Nnx6BGjowYo6ba
+19JRwHmRwvVTtnTOAiOL7wu9OHd/boPbLTLEcvTpAoGBAIrjkrrV6wQd41mCIhXx
+aTqA/m3Z/+OqMEAGwZhCN/u4IprcdvhviOtOJnfSJIVi8NayRJ96F0NjNYJvol6O
+gzR25MFazsDF1WqU7kO5Z9LI3cUq11ChnjM4pBpFnaGeCL5jiM5KYRZL+jjz3iLZ
+4dIOj5XwbSpgiTBrWlctf87U
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIEJ36MAjANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTZaFw0yNjExMTgwMDM1NTZaMF4xEjAQBgNVBAMTCWxvY2FsaG9zdDEUMBIGA1UE
+CxMLRGV2ZWxvcG1lbnQxEDAOBgNVBAoTB0FrbGl2dHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAxDIDiHLIyAh9dI2JmhUK1ZKAnHOEjZIul2Ux5d3nnnb/qziZqESpLgTXNvh0
+n6og7X6zPa/+D5VXMMKANuTWgIbhe2ufZ5+fRqXcEn+lR/v1D6oKxeWQbeynsNxR
+RYNEP3OSccAu/MbJTycA3UfbaiQ1eZa5Y6Lv117puh9Ft7aPCe69n2j/ZRzwsj07
+u3XRjBSaWZ5tpFemr7X4qnbCkoNcrFwQi6GGhmV7iHvW9wbgByll25F60VqTdA6h
+VUU21UYqy5vhkRz046gjtXd75H6Rj3tLFYoKx97B/GePusUIgy1fIWDeD3xSuq7P
+0A4AXC0V4KCMTRo+P40wXlibDQIDAQABo2gwZjAdBgNVHQ4EFgQU1/dB6JLv7zHW
+MMiE5S3Gmq1JUNowDgYDVR0PAQH/BAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9z
+dDAfBgNVHSMEGDAWgBRConr3AcDWR+WtUfnXRjz/2YgPmDANBgkqhkiG9w0BAQsF
+AAOCAQEAFjiQb6eJgD83LjM3AIa1wLFAYadtxVrIKC0m59n8quXGUEqBgMgoIgeV
+oBoVrtFxgbpmCxYpQpIdLX5jxCFnxvUUB0+NOIGGnR/mJx3qIq+ki3w5fNRSKyZi
+6h4ic75SICYlbLh0fM/5kr31K9ZzdKzd/c9rFHAkz0DtIBlYGmE/p7NzXHKu27f9
+prkHH7rCrnij/lzD+nvkQR1lYHFvaPbuVf738jepM6jcS48ik7p4eNlqqj7s40Mo
+4oW2aCzIe0RkGiT/9qdvXQxNumocaOgBtVuuWjgqvEZkIDy5ru5KZBiDdgRMrStl
+zrhBhPLC/yM4zbl31vB36MIfTmKMKQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+5OQUWOWJqvzyJ8VIbBRDsQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/signers.serverca.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/signers.serverca.pem
new file mode 100644
index 0000000000..389ef06999
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/signers.serverca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIEEiflHDANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTNaFw0zMTEyMTIwMDM1NTNaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAlmDVsfeWIEjypnw6qs0eVwTeM46KqHEvl5ElOyDoZZcqqZQN/jMW/VqzTbLc
+zjYE0HqpZNTbaW80kQ1O/VipDmnousimKHg7QtN5KIhsIelnZSQWq8cV2rtSTFDk
+rArE659GPWCPr/OeLT3Nbde0p9psz3uh1HJYVWAbZxWOe3GflSC8pGxu3PirU/kP
+g89RKRyO5UsF4feHdkJJqUJ92Th4n34DKQcHuwJ3iYxhB9hOlvI4ESIxM+4eWW89
+o4p2B2Ctwt8rpHDoBsNADophBD5kMT4mv6l5J3kVYy65QH7OfUIH22ApFjABdhGj
+lCMYtvSCN1Y1lDBU5M9xrBdERwIDAQABozIwMDAdBgNVHQ4EFgQUQqJ69wHA1kfl
+rVH510Y8/9mID5gwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+a56t9nJWGJlZFa8T1pnf9vdAcoQoqZ8LgKcdcxvGDtGdr5QF8L6LOqoYKUvetHv0
+kdvht0fqv3AZivCVyDIpMw17E5mLu5vvdUQM4E+qLNF6SUhO3c/Elylt2/3YKNBM
+FgjV0OdepnPz7/0nGCFUJo1fV8obUQt005P/S/F8g6UsIubcb/V55hR9/9Pruvw8
+gqAWNjPJZ0+BlhTgI505K80JFJ7CWZCaseDSeAkXPhb+a29vP2cDsR6wKZeny4+f
+P+TPUku7wEo8v+Tr8L1Esmcoudn6Wq+N3ZBYFLH6T4kqP+0BkDoStFDonUFRWxXX
+5OQUWOWJqvzyJ8VIbBRDsQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/trust.clientca.pem b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/trust.clientca.pem
new file mode 100644
index 0000000000..8cac297081
--- /dev/null
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/config/stores/server/trust.clientca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIECLJx+DANBgkqhkiG9w0BAQsFADBeMREwDwYDVQQDEwhz
+ZXJ2ZXJjYTEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxETAPBgNVBAoTCEFrbGl2aXR5
+MRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzAeFw0yMTEyMTQwMDM1
+NTRaFw0zMTEyMTIwMDM1NTRaMF4xETAPBgNVBAMTCHNlcnZlcmNhMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDERMA8GA1UEChMIQWtsaXZpdHkxEzARBgNVBAgTCkNhbGlm
+b3JuaWExCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAsiCAp3sTYiCVjNI3srcuHPa3p7BzGmxqMHeCqOU/ATABYTl9JTFEdqPfUGgS
+ubiDQ/DRf0pLmzsJL36eZq3mP7qvbuBE00rqWp4GfYYI8m+3yvVO0a/igLh0kRPV
+y9zMXLwIrzMupyAvex5g0xbJ7REu0QONf/Ff8obcowLi0ophH5N05Fx4qU+9nosZ
+tuEcS8A7DDMbt9liBVeGNYPenUoH2o9eRZ0YjzoGUNXOLsAW+gT/fn3ZkR8EgUiE
+LUjpIpOGWOQo3ro74wvkffs6WjMckoWY+kKfJ9nzL6orIZ9F5lNHwmSfgVaK5hcg
+J00jZ4SA7pOvu3MtKcuf0zjzrQIDAQABozIwMDAdBgNVHQ4EFgQU5obnoO20A2wT
+KYH+uCuF4ZW6cCswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+QnKttaZqVCoui8jQlI8uG6T4Wdg5ElYvO05Kz6dpKCBAxxcDXu4B7B+65bqABVXw
+KBu70+oXW9TQHE1psBzKeUeMKk8BMV+q8gCFWHNu5tpgp51xqwQGFFOJTm1HiV+S
+/WcnrnZe7XIa/bXbdYHz0uQV9fjMCqw354YGRRd1WWkHZYWrp9YucscpTKdGFQJD
+uM+poi57NOANSTgTyyZg3cerZTJjBL92XWDfMv93ZVjUE76B6bXvMJLG3Gh9nYEC
+epvLX8PSLdbal8ZBcosduxy1dSiQzy7Hj5wsOuj8neoVRaCHmAXouiJDDpQcnasC
+p0XXN2RYSD7FW7RNb4xcNQ==
+-----END CERTIFICATE-----
diff --git a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/schema/tls.schema.patch.json b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/schema/tls.schema.patch.json
index 14fee5bc53..0bf4c692eb 100644
--- a/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/schema/tls.schema.patch.json
+++ b/specs/binding-tls.spec/src/main/scripts/io/aklivity/zilla/specs/binding/tls/schema/tls.schema.patch.json
@@ -184,71 +184,83 @@
"path": "/$defs/options/binding/tls",
"value":
{
- "properties":
+ "properties":
+ {
+ "version":
+ {
+ "title": "Version",
+ "type": "string"
+ },
+ "keys":
{
- "version":
+ "title": "Vault Keys",
+ "type": "array",
+ "items":
{
- "title": "Version",
"type": "string"
- },
- "keys":
- {
- "title": "Vault Keys",
- "type": "array",
- "items":
- {
- "type": "string"
- }
- },
- "trust":
+ }
+ },
+ "trust":
+ {
+ "title": "Vault Certificates",
+ "type": "array",
+ "items":
{
- "title": "Vault Certificates",
- "type": "array",
- "items":
- {
- "type": "string"
- }
- },
- "trustcacerts":
+ "type": "string"
+ }
+ },
+ "trustcacerts":
+ {
+ "title": "Trust CA Certificates",
+ "type": "boolean"
+ },
+ "sni":
+ {
+ "title": "Server Names",
+ "type": "array",
+ "items":
{
- "title": "Trust CA Certificates",
- "type": "boolean"
- },
- "sni":
+ "type": "string"
+ }
+ },
+ "alpn":
+ {
+ "title": "Application Protocols",
+ "type": "array",
+ "items":
{
- "title": "Server Names",
- "type": "array",
- "items":
- {
- "type": "string"
- }
- },
- "alpn":
+ "type": [ "string", "null" ]
+ }
+ },
+ "mutual":
+ {
+ "title": "Mutual Authentication",
+ "type": "string",
+ "enum": [ "required", "requested", "none" ]
+ },
+ "signers":
+ {
+ "title": "Vault Signer Certificates",
+ "type": "array",
+ "items":
{
- "title": "Application Protocols",
- "type": "array",
- "items":
- {
- "type": [ "string", "null" ]
- }
- },
- "mutual":
+ "type": "string"
+ }
+ }
+ },
+ "not":
+ {
+ "allOf":
+ [
{
- "title": "Mutual Authentication",
- "type": "string",
- "enum": [ "required", "requested", "none" ]
+ "required": ["keys"]
},
- "signers":
{
- "title": "Vault Signer Certificates",
- "type": "array",
- "items":
- {
- "type": "string"
- }
+ "required": ["signers"]
}
- },
- "additionalProperties": false
+ ]
+ },
+ "additionalProperties": false
}
}
]
diff --git a/specs/binding-tls.spec/src/test/java/io/aklivity/zilla/specs/binding/tls/config/SchemaTest.java b/specs/binding-tls.spec/src/test/java/io/aklivity/zilla/specs/binding/tls/config/SchemaTest.java
index 16333bb48e..7ec1669ff4 100644
--- a/specs/binding-tls.spec/src/test/java/io/aklivity/zilla/specs/binding/tls/config/SchemaTest.java
+++ b/specs/binding-tls.spec/src/test/java/io/aklivity/zilla/specs/binding/tls/config/SchemaTest.java
@@ -21,7 +21,6 @@
import jakarta.json.JsonObject;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -32,7 +31,7 @@ public class SchemaTest
@Rule
public final ConfigSchemaRule schema = new ConfigSchemaRule()
.schemaPatch("io/aklivity/zilla/specs/binding/tls/schema/tls.schema.patch.json")
- .schemaPatch("io/aklivity/zilla/specs/vault/filesystem/schema/filesystem.schema.patch.json")
+ .schemaPatch("io/aklivity/zilla/specs/engine/schema/vault/test.schema.patch.json")
.configurationRoot("io/aklivity/zilla/specs/binding/tls/config");
@Test
@@ -155,15 +154,6 @@ public void shouldValidateServerMutualRequested()
assertThat(config, not(nullValue()));
}
- @Ignore("TODO: realms")
- @Test
- public void shouldValidateServerRealm()
- {
- JsonObject config = schema.validate("server.realm.yaml");
-
- assertThat(config, not(nullValue()));
- }
-
@Test
public void shouldValidateServerSigner()
{
diff --git a/specs/binding-ws.spec/pom.xml b/specs/binding-ws.spec/pom.xml
index 8ef5cba3d0..7fcff636f8 100644
--- a/specs/binding-ws.spec/pom.xml
+++ b/specs/binding-ws.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/catalog-apicurio.spec/pom.xml b/specs/catalog-apicurio.spec/pom.xml
index 63c9bdc519..41a5615f07 100644
--- a/specs/catalog-apicurio.spec/pom.xml
+++ b/specs/catalog-apicurio.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/catalog-filesystem.spec/pom.xml b/specs/catalog-filesystem.spec/pom.xml
index 8df20fe530..5b132c0b52 100644
--- a/specs/catalog-filesystem.spec/pom.xml
+++ b/specs/catalog-filesystem.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/catalog-inline.spec/pom.xml b/specs/catalog-inline.spec/pom.xml
index 7a8269a1f1..54f79dec4b 100644
--- a/specs/catalog-inline.spec/pom.xml
+++ b/specs/catalog-inline.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/catalog-karapace.spec/pom.xml b/specs/catalog-karapace.spec/pom.xml
index 1ee29c8112..bcd878e8d1 100644
--- a/specs/catalog-karapace.spec/pom.xml
+++ b/specs/catalog-karapace.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/catalog-schema-registry.spec/pom.xml b/specs/catalog-schema-registry.spec/pom.xml
index 3cc96941e6..fe7979bb88 100644
--- a/specs/catalog-schema-registry.spec/pom.xml
+++ b/specs/catalog-schema-registry.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/engine.spec/pom.xml b/specs/engine.spec/pom.xml
index 5379216018..d6296b1b16 100644
--- a/specs/engine.spec/pom.xml
+++ b/specs/engine.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/binding/test.schema.patch.json b/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/binding/test.schema.patch.json
index dfbd5c307e..198f370b19 100644
--- a/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/binding/test.schema.patch.json
+++ b/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/binding/test.schema.patch.json
@@ -59,36 +59,60 @@
},
"assertions":
{
- "catalog":
+ "title": "Assertions",
+ "type": "object",
+ "properties":
{
- "type": "object",
- "patternProperties":
+ "catalog":
{
- "^[a-zA-Z]+[a-zA-Z0-9\\._\\-]*$":
+ "type": "object",
+ "patternProperties":
{
- "type": "array",
- "items":
+ "^[a-zA-Z]+[a-zA-Z0-9\\._\\-]*$":
{
- "type": "object",
- "properties":
+ "type": "array",
+ "items":
{
- "id":
+ "type": "object",
+ "properties":
{
- "type": "integer"
- },
- "schema":
- {
- "type": ["string", "null"]
- },
- "delay":
- {
- "type": "number"
+ "id":
+ {
+ "type": "integer"
+ },
+ "schema":
+ {
+ "type": ["string", "null"]
+ },
+ "delay":
+ {
+ "type": "number"
+ }
}
}
}
- }
+ },
+ "maxProperties": 1
},
- "maxProperties": 1
+ "vault":
+ {
+ "type": "object",
+ "properties":
+ {
+ "key":
+ {
+ "type": "String"
+ },
+ "signer":
+ {
+ "type": "String"
+ },
+ "trust":
+ {
+ "type": "String"
+ }
+ }
+ }
}
},
"port":
diff --git a/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/vault/test.schema.patch.json b/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/vault/test.schema.patch.json
index 01851cebc6..1bcb67068c 100644
--- a/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/vault/test.schema.patch.json
+++ b/specs/engine.spec/src/main/scripts/io/aklivity/zilla/specs/engine/schema/vault/test.schema.patch.json
@@ -26,6 +26,73 @@
"type":
{
"const": "test"
+ },
+ "options":
+ {
+ "type": "object",
+ "properties":
+ {
+ "key":
+ {
+ "type": "object",
+ "properties":
+ {
+ "alias":
+ {
+ "type": "string"
+ },
+ "entry":
+ {
+ "type": "string"
+ }
+ },
+ "required":
+ [
+ "alias",
+ "entry"
+ ]
+ },
+ "signer":
+ {
+ "type": "object",
+ "properties":
+ {
+ "alias":
+ {
+ "type": "string"
+ },
+ "entry":
+ {
+ "type": "string"
+ }
+ },
+ "required":
+ [
+ "alias",
+ "entry"
+ ]
+ },
+ "trust":
+ {
+ "type": "object",
+ "properties":
+ {
+ "alias":
+ {
+ "type": "string"
+ },
+ "entry":
+ {
+ "type": "string"
+ }
+ },
+ "required":
+ [
+ "alias",
+ "entry"
+ ]
+ }
+ }
}
}
}
diff --git a/specs/exporter-otlp.spec/pom.xml b/specs/exporter-otlp.spec/pom.xml
index 5d8c89b3d7..6e74e01ec8 100644
--- a/specs/exporter-otlp.spec/pom.xml
+++ b/specs/exporter-otlp.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/exporter-prometheus.spec/pom.xml b/specs/exporter-prometheus.spec/pom.xml
index 2b1ce460c4..f567f3c71c 100644
--- a/specs/exporter-prometheus.spec/pom.xml
+++ b/specs/exporter-prometheus.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/exporter-stdout.spec/pom.xml b/specs/exporter-stdout.spec/pom.xml
index 3ba1b32ec2..36d7479746 100644
--- a/specs/exporter-stdout.spec/pom.xml
+++ b/specs/exporter-stdout.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/filesystem-http.spec/pom.xml b/specs/filesystem-http.spec/pom.xml
index e122a56f03..a9de7783ed 100644
--- a/specs/filesystem-http.spec/pom.xml
+++ b/specs/filesystem-http.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/guard-jwt.spec/pom.xml b/specs/guard-jwt.spec/pom.xml
index e700fc1123..75a66501e5 100644
--- a/specs/guard-jwt.spec/pom.xml
+++ b/specs/guard-jwt.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/metrics-grpc.spec/pom.xml b/specs/metrics-grpc.spec/pom.xml
index 03c6b37b72..7382463c66 100644
--- a/specs/metrics-grpc.spec/pom.xml
+++ b/specs/metrics-grpc.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/metrics-http.spec/pom.xml b/specs/metrics-http.spec/pom.xml
index d92f1ada48..f3a1fe8e1f 100644
--- a/specs/metrics-http.spec/pom.xml
+++ b/specs/metrics-http.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/metrics-stream.spec/pom.xml b/specs/metrics-stream.spec/pom.xml
index 1f55b14030..eff89f4c9c 100644
--- a/specs/metrics-stream.spec/pom.xml
+++ b/specs/metrics-stream.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/model-avro.spec/pom.xml b/specs/model-avro.spec/pom.xml
index 72ceacd485..6c55dd2e0f 100644
--- a/specs/model-avro.spec/pom.xml
+++ b/specs/model-avro.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/model-core.spec/pom.xml b/specs/model-core.spec/pom.xml
index 55c2285b91..b3c9c588d8 100644
--- a/specs/model-core.spec/pom.xml
+++ b/specs/model-core.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/model-json.spec/pom.xml b/specs/model-json.spec/pom.xml
index 36ba6413e0..e09355ca5e 100644
--- a/specs/model-json.spec/pom.xml
+++ b/specs/model-json.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/model-protobuf.spec/pom.xml b/specs/model-protobuf.spec/pom.xml
index 8b5a9c977d..8be991d02c 100644
--- a/specs/model-protobuf.spec/pom.xml
+++ b/specs/model-protobuf.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/pom.xml b/specs/pom.xml
index 8b82c73e9a..a6bc134f00 100644
--- a/specs/pom.xml
+++ b/specs/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
zilla
- 0.9.92
+ 0.9.93
../pom.xml
diff --git a/specs/vault-filesystem.spec/pom.xml b/specs/vault-filesystem.spec/pom.xml
index 0411eeb1f3..14bf2f8a53 100644
--- a/specs/vault-filesystem.spec/pom.xml
+++ b/specs/vault-filesystem.spec/pom.xml
@@ -8,7 +8,7 @@
io.aklivity.zilla
specs
- 0.9.92
+ 0.9.93
../pom.xml