diff --git a/servicetalk-examples/docs/modules/ROOT/pages/_partials/nav-versioned.adoc b/servicetalk-examples/docs/modules/ROOT/pages/_partials/nav-versioned.adoc index 6881a6db46..22ff8df5a5 100644 --- a/servicetalk-examples/docs/modules/ROOT/pages/_partials/nav-versioned.adoc +++ b/servicetalk-examples/docs/modules/ROOT/pages/_partials/nav-versioned.adoc @@ -5,6 +5,7 @@ ** xref:{page-version}@servicetalk-examples::http/index.adoc#Debugging[Debugging] ** xref:{page-version}@servicetalk-examples::http/index.adoc#Timeout[Timeout] ** xref:{page-version}@servicetalk-examples::http/index.adoc#SerializationJson[Serialization: JSON] +** xref:{page-version}@servicetalk-examples::http/index.adoc#SerializationProtobuf[Serialization: Protobuf] ** xref:{page-version}@servicetalk-examples::http/index.adoc#JAXRS[JAX-RS] ** xref:{page-version}@servicetalk-examples::http/index.adoc#MetaData[MetaData] ** xref:{page-version}@servicetalk-examples::http/index.adoc#HTTP2[HTTP/2] diff --git a/servicetalk-examples/docs/modules/ROOT/pages/http/index.adoc b/servicetalk-examples/docs/modules/ROOT/pages/http/index.adoc index f90705d6e3..023ef14700 100644 --- a/servicetalk-examples/docs/modules/ROOT/pages/http/index.adoc +++ b/servicetalk-examples/docs/modules/ROOT/pages/http/index.adoc @@ -118,6 +118,22 @@ with `Content-Type: application/json` and link:{source-root}/servicetalk-example All serializers and deserializers defined in link:{source-root}/servicetalk-examples/http/serialization/json/src/main/java/io/servicetalk/examples/http/serialization/json/SerializerUtils.java[SerializerUtils]. +[#SerializationProtobuf] +== Serialization: Protobuf + +An example similar to "Hello World" examples, which demonstrates +link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async[asynchronous-aggregated], +link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming[asynchronous-streaming], +link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking[blocking-aggregated], and +link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming[blocking-streaming] +client and server with Protobuf serialization of simple proto objects. + +Client sends a `POST` request with a Protobuf payload `RequestMessage` and expects a response with +`Content-Type: application/protobuf` and `ResponseMessage` as a payload +(link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/proto/message.proto[message.proto]). +All serializers and deserializers defined in +link:{source-root}/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/SerializerUtils.java[SerializerUtils]. + [#JAXRS] == JAX-RS diff --git a/servicetalk-examples/http/serialization/protobuf/build.gradle b/servicetalk-examples/http/serialization/protobuf/build.gradle new file mode 100644 index 0000000000..9a186f88e6 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ + +buildscript { + dependencies { + classpath "com.google.protobuf:protobuf-gradle-plugin:$protobufGradlePluginVersion" + } +} + +apply plugin: "java" +apply from: "../../../gradle/idea.gradle" +apply plugin: "com.google.protobuf" + +dependencies { + implementation project(":servicetalk-annotations") + implementation project(":servicetalk-data-protobuf") + implementation project(":servicetalk-http-netty") + implementation "com.google.protobuf:protobuf-java:$protobufVersion" + + runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion" +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:$protobufVersion" + } +} + +clean { + delete protobuf.generatedFilesBaseDir +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/SerializerUtils.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/SerializerUtils.java new file mode 100644 index 0000000000..c92cea662b --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/SerializerUtils.java @@ -0,0 +1,69 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf; + +import io.servicetalk.data.protobuf.ProtobufSerializerFactory; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.api.HttpHeaders; +import io.servicetalk.http.api.HttpSerializerDeserializer; +import io.servicetalk.http.api.HttpSerializers; +import io.servicetalk.http.api.HttpStreamingSerializerDeserializer; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +import static io.servicetalk.buffer.api.CharSequences.newAsciiString; +import static io.servicetalk.data.protobuf.ProtobufSerializerFactory.PROTOBUF; +import static io.servicetalk.http.api.HeaderUtils.hasContentType; +import static io.servicetalk.http.api.HttpHeaderNames.CONTENT_TYPE; +import static io.servicetalk.http.api.HttpSerializers.serializer; +import static io.servicetalk.http.api.HttpSerializers.streamingSerializer; + +/** + * Utilities to cache serializer instances for request/response protos. + *

+ * {@link ProtobufSerializerFactory} produces protocol-agnostic serializer/deserializer. We use {@link HttpSerializers} + * utilities to concert it to HTTP-aware variants. + */ +public final class SerializerUtils { + + private static final CharSequence APPLICATION_PROTOBUF = newAsciiString("application/protobuf"); + private static final Consumer CONTENT_TYPE_SETTER = + headers -> headers.set(CONTENT_TYPE, APPLICATION_PROTOBUF); + private static final Predicate CONTENT_TYPE_VALIDATOR = + headers -> hasContentType(headers, APPLICATION_PROTOBUF, null); + + public static final HttpSerializerDeserializer REQ_SERIALIZER = + serializer(PROTOBUF.serializerDeserializer(RequestMessage.parser()), + CONTENT_TYPE_SETTER, CONTENT_TYPE_VALIDATOR); + + public static final HttpStreamingSerializerDeserializer REQ_STREAMING_SERIALIZER = + streamingSerializer(PROTOBUF.streamingSerializerDeserializer(RequestMessage.parser()), + CONTENT_TYPE_SETTER, CONTENT_TYPE_VALIDATOR); + + public static final HttpSerializerDeserializer RESP_SERIALIZER = + serializer(PROTOBUF.serializerDeserializer(ResponseMessage.parser()), + CONTENT_TYPE_SETTER, CONTENT_TYPE_VALIDATOR); + + public static final HttpStreamingSerializerDeserializer RESP_STREAMING_SERIALIZER = + streamingSerializer(PROTOBUF.streamingSerializerDeserializer(ResponseMessage.parser()), + CONTENT_TYPE_SETTER, CONTENT_TYPE_VALIDATOR); + + private SerializerUtils() { + // No instances. + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufClient.java new file mode 100644 index 0000000000..200a4060e9 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufClient.java @@ -0,0 +1,40 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.http.api.HttpClient; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; + +public final class ProtobufClient { + public static void main(String[] args) throws Exception { + try (HttpClient client = HttpClients.forSingleAddress("localhost", 8080).build()) { + client.request(client.post("/protobuf") + .payloadBody(RequestMessage.newBuilder().setMessage("value").build(), REQ_SERIALIZER)) + .whenOnSuccess(resp -> { + System.out.println(resp.toString((name, value) -> value)); + System.out.println(resp.payloadBody(RESP_SERIALIZER)); + }) + // This example is demonstrating asynchronous execution, but needs to prevent the main thread from exiting + // before the response has been processed. This isn't typical usage for an asynchronous API but is useful + // for demonstration purposes. + .toFuture().get(); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufServer.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufServer.java new file mode 100644 index 0000000000..685ca545c6 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufServer.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.netty.HttpServers; + +import static io.servicetalk.concurrent.api.Single.succeeded; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; +import static io.servicetalk.http.api.HttpHeaderNames.ALLOW; +import static io.servicetalk.http.api.HttpRequestMethod.POST; + +public final class ProtobufServer { + public static void main(String[] args) throws Exception { + HttpServers.forPort(8080) + .listenAndAwait((ctx, request, responseFactory) -> { + if (!"/protobuf".equals(request.requestTarget())) { + return succeeded(responseFactory.notFound()); + } + if (!POST.equals(request.method())) { + return succeeded(responseFactory.methodNotAllowed().addHeader(ALLOW, POST.name())); + } + RequestMessage req = request.payloadBody(REQ_SERIALIZER); + ResponseMessage resp = ResponseMessage.newBuilder().setLength(req.getMessage().length()).build(); + return succeeded(responseFactory.created() + .payloadBody(resp, RESP_SERIALIZER)); + }) + .awaitShutdown(); + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufUrlClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufUrlClient.java new file mode 100644 index 0000000000..da1208d2bf --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/ProtobufUrlClient.java @@ -0,0 +1,40 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos; +import io.servicetalk.http.api.HttpClient; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; + +public final class ProtobufUrlClient { + public static void main(String[] args) throws Exception { + try (HttpClient client = HttpClients.forMultiAddressUrl().build()) { + client.request(client.post("http://localhost:8080/protobuf") + .payloadBody(ExampleProtos.RequestMessage.newBuilder().setMessage("hello").build(), REQ_SERIALIZER)) + .whenOnSuccess(resp -> { + System.out.println(resp.toString((name, value) -> value)); + System.out.println(resp.payloadBody(RESP_SERIALIZER)); + }) + // This example is demonstrating asynchronous execution, but needs to prevent the main thread from exiting + // before the response has been processed. This isn't typical usage for an asynchronous API but is useful + // for demonstration purposes. + .toFuture().get(); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/package-info.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/package-info.java new file mode 100644 index 0000000000..b4e245f6e0 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ +@ElementsAreNonnullByDefault +package io.servicetalk.examples.http.serialization.protobuf.async; + +import io.servicetalk.annotations.ElementsAreNonnullByDefault; diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingClient.java new file mode 100644 index 0000000000..4a4b8e1c3c --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingClient.java @@ -0,0 +1,42 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async.streaming; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.http.api.StreamingHttpClient; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.concurrent.api.Publisher.from; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; + +public final class ProtobufStreamingClient { + public static void main(String[] args) throws Exception { + try (StreamingHttpClient client = HttpClients.forSingleAddress("localhost", 8080).buildStreaming()) { + client.request(client.post("/protobuf") + .payloadBody(from("value1", "value22", "value333") + .map(message -> RequestMessage.newBuilder().setMessage(message).build()), + REQ_STREAMING_SERIALIZER)) + .beforeOnSuccess(response -> System.out.println(response.toString((name, value) -> value))) + .flatMapPublisher(resp -> resp.payloadBody(RESP_STREAMING_SERIALIZER)) + .whenOnNext(System.out::println) + // This example is demonstrating asynchronous execution, but needs to prevent the main thread from exiting + // before the response has been processed. This isn't typical usage for an asynchronous API but is useful + // for demonstration purposes. + .toFuture().get(); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingServer.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingServer.java new file mode 100644 index 0000000000..d961c2deaf --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingServer.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async.streaming; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.netty.HttpServers; + +import static io.servicetalk.concurrent.api.Single.succeeded; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; +import static io.servicetalk.http.api.HttpHeaderNames.ALLOW; +import static io.servicetalk.http.api.HttpRequestMethod.POST; + +public final class ProtobufStreamingServer { + public static void main(String[] args) throws Exception { + HttpServers.forPort(8080) + .listenStreamingAndAwait((ctx, request, responseFactory) -> { + if (!"/protobuf".equals(request.requestTarget())) { + return succeeded(responseFactory.notFound()); + } + if (!POST.equals(request.method())) { + return succeeded(responseFactory.methodNotAllowed().addHeader(ALLOW, POST.name())); + } + return succeeded(responseFactory.created() + .payloadBody(request.payloadBody(REQ_STREAMING_SERIALIZER) + .map(req -> ResponseMessage.newBuilder().setLength(req.getMessage().length()).build()), + RESP_STREAMING_SERIALIZER)); + }) + .awaitShutdown(); + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingUrlClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingUrlClient.java new file mode 100644 index 0000000000..512357a78b --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/ProtobufStreamingUrlClient.java @@ -0,0 +1,42 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.async.streaming; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.http.api.StreamingHttpClient; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.concurrent.api.Publisher.from; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; + +public final class ProtobufStreamingUrlClient { + public static void main(String[] args) throws Exception { + try (StreamingHttpClient client = HttpClients.forMultiAddressUrl().buildStreaming()) { + client.request(client.post("http://localhost:8080/protobuf") + .payloadBody(from("value1", "value22", "value333") + .map(message -> RequestMessage.newBuilder().setMessage(message).build()), + REQ_STREAMING_SERIALIZER)) + .beforeOnSuccess(response -> System.out.println(response.toString((name, value) -> value))) + .flatMapPublisher(resp -> resp.payloadBody(RESP_STREAMING_SERIALIZER)) + .whenOnNext(System.out::println) + // This example is demonstrating asynchronous execution, but needs to prevent the main thread from exiting + // before the response has been processed. This isn't typical usage for an asynchronous API but is useful + // for demonstration purposes. + .toFuture().get(); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/package-info.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/package-info.java new file mode 100644 index 0000000000..a85fcba2f6 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/async/streaming/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ +@ElementsAreNonnullByDefault +package io.servicetalk.examples.http.serialization.protobuf.async.streaming; + +import io.servicetalk.annotations.ElementsAreNonnullByDefault; diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufClient.java new file mode 100644 index 0000000000..870db1c795 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufClient.java @@ -0,0 +1,35 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.http.api.BlockingHttpClient; +import io.servicetalk.http.api.HttpResponse; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; + +public final class BlockingProtobufClient { + public static void main(String[] args) throws Exception { + try (BlockingHttpClient client = HttpClients.forSingleAddress("localhost", 8080).buildBlocking()) { + HttpResponse resp = client.request(client.post("/protobuf") + .payloadBody(RequestMessage.newBuilder().setMessage("value").build(), REQ_SERIALIZER)); + System.out.println(resp.toString((name, value) -> value)); + System.out.println(resp.payloadBody(RESP_SERIALIZER)); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufServer.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufServer.java new file mode 100644 index 0000000000..2e207d497c --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufServer.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.netty.HttpServers; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; +import static io.servicetalk.http.api.HttpHeaderNames.ALLOW; +import static io.servicetalk.http.api.HttpRequestMethod.POST; + +public final class BlockingProtobufServer { + public static void main(String[] args) throws Exception { + HttpServers.forPort(8080) + .listenBlockingAndAwait((ctx, request, responseFactory) -> { + if (!"/protobuf".equals(request.requestTarget())) { + return responseFactory.notFound(); + } + if (!POST.equals(request.method())) { + return responseFactory.methodNotAllowed().addHeader(ALLOW, POST.name()); + } + + RequestMessage req = request.payloadBody(REQ_SERIALIZER); + ResponseMessage resp = ResponseMessage.newBuilder().setLength(req.getMessage().length()).build(); + return responseFactory.created() + .payloadBody(resp, RESP_SERIALIZER); + }) + .awaitShutdown(); + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufUrlClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufUrlClient.java new file mode 100644 index 0000000000..72cfe83550 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/BlockingProtobufUrlClient.java @@ -0,0 +1,35 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking; + +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.http.api.BlockingHttpClient; +import io.servicetalk.http.api.HttpResponse; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_SERIALIZER; + +public final class BlockingProtobufUrlClient { + public static void main(String[] args) throws Exception { + try (BlockingHttpClient client = HttpClients.forMultiAddressUrl().buildBlocking()) { + HttpResponse resp = client.request(client.post("http://localhost:8080/protobuf") + .payloadBody(RequestMessage.newBuilder().setMessage("value").build(), REQ_SERIALIZER)); + System.out.println(resp.toString((name, value) -> value)); + System.out.println(resp.payloadBody(RESP_SERIALIZER)); + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/package-info.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/package-info.java new file mode 100644 index 0000000000..9d5871561e --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ +@ElementsAreNonnullByDefault +package io.servicetalk.examples.http.serialization.protobuf.blocking; + +import io.servicetalk.annotations.ElementsAreNonnullByDefault; diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingClient.java new file mode 100644 index 0000000000..821f80b8ee --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingClient.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking.streaming; + +import io.servicetalk.concurrent.BlockingIterator; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.api.BlockingStreamingHttpClient; +import io.servicetalk.http.api.BlockingStreamingHttpResponse; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; +import static java.util.Arrays.asList; + +public final class BlockingProtobufStreamingClient { + public static void main(String[] args) throws Exception { + try (BlockingStreamingHttpClient client = + HttpClients.forSingleAddress("localhost", 8080).buildBlockingStreaming()) { + BlockingStreamingHttpResponse response = client.request(client.post("/protobuf") + .payloadBody(asList( + RequestMessage.newBuilder().setMessage("value1").build(), + RequestMessage.newBuilder().setMessage("value22").build(), + RequestMessage.newBuilder().setMessage("value333").build()), + REQ_STREAMING_SERIALIZER)); + System.out.println(response.toString((name, value) -> value)); + // While it's also possible to use for-each, it's recommended to use try-with-resources to make sure that + // the full response payload body is drained in case of exceptions + try (BlockingIterator payload = + response.payloadBody(RESP_STREAMING_SERIALIZER).iterator()) { + while (payload.hasNext()) { + System.out.println(payload.next()); + } + } + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingServer.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingServer.java new file mode 100644 index 0000000000..c031810cb9 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingServer.java @@ -0,0 +1,59 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking.streaming; + +import io.servicetalk.concurrent.BlockingIterable; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.api.HttpPayloadWriter; +import io.servicetalk.http.netty.HttpServers; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; +import static io.servicetalk.http.api.HttpHeaderNames.ALLOW; +import static io.servicetalk.http.api.HttpRequestMethod.POST; +import static io.servicetalk.http.api.HttpResponseStatus.CREATED; +import static io.servicetalk.http.api.HttpResponseStatus.METHOD_NOT_ALLOWED; +import static io.servicetalk.http.api.HttpResponseStatus.NOT_FOUND; + +public final class BlockingProtobufStreamingServer { + public static void main(String[] args) throws Exception { + HttpServers.forPort(8080) + .listenBlockingStreamingAndAwait((ctx, request, response) -> { + if (!"/protobuf".equals(request.requestTarget())) { + response.status(NOT_FOUND) + .sendMetaData() + .close(); + } else if (!POST.equals(request.method())) { + response.status(METHOD_NOT_ALLOWED) + .addHeader(ALLOW, POST.name()) + .sendMetaData() + .close(); + } else { + BlockingIterable values = request.payloadBody(REQ_STREAMING_SERIALIZER); + + response.status(CREATED); + try (HttpPayloadWriter writer = + response.sendMetaData(RESP_STREAMING_SERIALIZER)) { + for (RequestMessage req : values) { + writer.write(ResponseMessage.newBuilder().setLength(req.getMessage().length()).build()); + } + } + } + }) + .awaitShutdown(); + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingUrlClient.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingUrlClient.java new file mode 100644 index 0000000000..dcd76a47f4 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/BlockingProtobufStreamingUrlClient.java @@ -0,0 +1,49 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.servicetalk.examples.http.serialization.protobuf.blocking.streaming; + +import io.servicetalk.concurrent.BlockingIterator; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.RequestMessage; +import io.servicetalk.examples.http.serialization.protobuf.ExampleProtos.ResponseMessage; +import io.servicetalk.http.api.BlockingStreamingHttpClient; +import io.servicetalk.http.api.BlockingStreamingHttpResponse; +import io.servicetalk.http.netty.HttpClients; + +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.REQ_STREAMING_SERIALIZER; +import static io.servicetalk.examples.http.serialization.protobuf.SerializerUtils.RESP_STREAMING_SERIALIZER; +import static java.util.Arrays.asList; + +public final class BlockingProtobufStreamingUrlClient { + public static void main(String[] args) throws Exception { + try (BlockingStreamingHttpClient client = HttpClients.forMultiAddressUrl().buildBlockingStreaming()) { + BlockingStreamingHttpResponse response = client.request(client.post("http://localhost:8080/protobuf") + .payloadBody(asList( + RequestMessage.newBuilder().setMessage("value1").build(), + RequestMessage.newBuilder().setMessage("value22").build(), + RequestMessage.newBuilder().setMessage("value333").build()), + REQ_STREAMING_SERIALIZER)); + System.out.println(response.toString((name, value) -> value)); + // While it's also possible to use for-each, it's recommended to use try-with-resources to make sure that + // the full response payload body is drained in case of exceptions + try (BlockingIterator payload = + response.payloadBody(RESP_STREAMING_SERIALIZER).iterator()) { + while (payload.hasNext()) { + System.out.println(payload.next()); + } + } + } + } +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/package-info.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/package-info.java new file mode 100644 index 0000000000..e51a0ead22 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/blocking/streaming/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ +@ElementsAreNonnullByDefault +package io.servicetalk.examples.http.serialization.protobuf.blocking.streaming; + +import io.servicetalk.annotations.ElementsAreNonnullByDefault; diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/package-info.java b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/package-info.java new file mode 100644 index 0000000000..97b1be9296 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/java/io/servicetalk/examples/http/serialization/protobuf/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ +@ElementsAreNonnullByDefault +package io.servicetalk.examples.http.serialization.protobuf; + +import io.servicetalk.annotations.ElementsAreNonnullByDefault; diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/proto/message.proto b/servicetalk-examples/http/serialization/protobuf/src/main/proto/message.proto new file mode 100644 index 0000000000..596411ebc7 --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/proto/message.proto @@ -0,0 +1,30 @@ +// +// Copyright © 2022 Apple Inc. and the ServiceTalk project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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. + +syntax = "proto3"; + +package test.shared; + +option java_multiple_files = false; +option java_package = "io.servicetalk.examples.http.serialization.protobuf"; +option java_outer_classname = "ExampleProtos"; + +message RequestMessage { + string message = 1; +} + +message ResponseMessage { + int32 length = 1; +} diff --git a/servicetalk-examples/http/serialization/protobuf/src/main/resources/log4j2.xml b/servicetalk-examples/http/serialization/protobuf/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..3e1a0311eb --- /dev/null +++ b/servicetalk-examples/http/serialization/protobuf/src/main/resources/log4j2.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index d09a0bf066..40f166e56c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -58,6 +58,7 @@ include "servicetalk-annotations", "servicetalk-examples:http:observer", "servicetalk-examples:http:retry", "servicetalk-examples:http:serialization:json", + "servicetalk-examples:http:serialization:protobuf", "servicetalk-examples:http:service-composition", "servicetalk-examples:http:uds", "servicetalk-examples:http:compression", @@ -122,6 +123,7 @@ project(":servicetalk-examples:http:opentracing").name = "servicetalk-examples-h project(":servicetalk-examples:http:observer").name = "servicetalk-examples-http-observer" project(":servicetalk-examples:http:retry").name = "servicetalk-examples-http-retry" project(":servicetalk-examples:http:serialization:json").name = "servicetalk-examples-http-serialization-json" +project(":servicetalk-examples:http:serialization:protobuf").name = "servicetalk-examples-http-serialization-protobuf" project(":servicetalk-examples:http:service-composition").name = "servicetalk-examples-http-service-composition" project(":servicetalk-examples:http:uds").name = "servicetalk-examples-http-uds" project(":servicetalk-examples:http:mutual-tls").name = "servicetalk-examples-http-mutual-tls"