Skip to content

Commit

Permalink
Add support for gRPC transcoding
Browse files Browse the repository at this point in the history
  • Loading branch information
zZHorizonZz committed Jan 23, 2025
1 parent 723c025 commit f4aa5c5
Show file tree
Hide file tree
Showing 48 changed files with 4,271 additions and 47 deletions.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@

<modules>
<module>vertx-grpc-common</module>
<module>vertx-grpc-transcoding</module>
<module>vertx-grpc-server</module>
<module>vertx-grpc-client</module>
<module>vertx-grpcio-common</module>
Expand Down Expand Up @@ -271,4 +272,4 @@
</plugins>
</build>

</project>
</project>
2 changes: 2 additions & 0 deletions vertx-grpc-docs/src/main/asciidoc/plugin.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,5 @@ Vert.x gRPC needs to know to interact with gRPC.
- the message encoder

They can be used to bind services or interact with a remote server.

=== Generate transcoding definitions
6 changes: 6 additions & 0 deletions vertx-grpc-docs/src/main/asciidoc/server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ router.route("/com.mycompany.MyService/*").handler(corsHandler);
----
====

==== gRPC Transcoding

The Vert.x gRPC Server supports the gRPC transcoding. The transcoding is disabled by default.

To enable the gRPC transcoding, configure options with {@link io.vertx.grpc.server.GrpcServerOptions#setGrpcTranscodingEnabled GrpcServerOptions#setGrpcTranscodingEnabled(true)} and then create a server with {@link io.vertx.grpc.server.GrpcServer#server(io.vertx.core.Vertx, io.vertx.grpc.server.GrpcServerOptions) GrpcServer#server(vertx, options)}.

=== Server request/response API

The gRPC request/response server API provides an alternative way to interact with a client without the need of extending
Expand Down
38 changes: 38 additions & 0 deletions vertx-grpc-docs/src/main/java/examples/VertxGreeterGrpcServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
Expand All @@ -13,6 +14,7 @@
import io.vertx.grpc.common.GrpcWriteStream;
import io.vertx.grpc.common.GrpcMessageDecoder;
import io.vertx.grpc.common.GrpcMessageEncoder;
import io.vertx.grpc.transcoding.ServiceTranscodingOptions;
import io.vertx.grpc.server.GrpcServerResponse;
import io.vertx.grpc.server.GrpcServer;

Expand All @@ -31,6 +33,14 @@ public class VertxGreeterGrpcServer {
"SayHello",
GrpcMessageEncoder.json(),
GrpcMessageDecoder.json(() -> examples.HelloRequest.newBuilder()));
public static final ServiceTranscodingOptions SayHello_TRANSCODING = ServiceTranscodingOptions.create(
"",
HttpMethod.valueOf("POST"),
"/Greeter/SayHello",
"",
"",
List.of(
));

public static class GreeterApi {

Expand Down Expand Up @@ -74,6 +84,24 @@ public GreeterApi bind_sayHello(GrpcServer server, io.vertx.grpc.common.WireForm
server.callHandler(serviceMethod, this::handle_sayHello);
return this;
}
public GreeterApi bind_sayHello_with_transcoding(GrpcServer server) {
return bind_sayHello_with_transcoding(server, io.vertx.grpc.common.WireFormat.PROTOBUF);
}
public GreeterApi bind_sayHello_with_transcoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
ServiceMethod<examples.HelloRequest,examples.HelloReply> serviceMethod;
switch(format) {
case PROTOBUF:
serviceMethod = SayHello;
break;
case JSON:
serviceMethod = SayHello_JSON;
break;
default:
throw new AssertionError();
}
server.callHandlerWithTranscoding(serviceMethod, this::handle_sayHello, SayHello_TRANSCODING);
return this;
}

public final GreeterApi bindAll(GrpcServer server) {
bind_sayHello(server);
Expand All @@ -84,5 +112,15 @@ public final GreeterApi bindAll(GrpcServer server, io.vertx.grpc.common.WireForm
bind_sayHello(server, format);
return this;
}

public final GreeterApi bindAllWithTranscoding(GrpcServer server) {
bind_sayHello_with_transcoding(server);
return this;
}

public final GreeterApi bindAllWithTranscoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
bind_sayHello_with_transcoding(server, format);
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
Expand All @@ -13,6 +14,7 @@
import io.vertx.grpc.common.GrpcWriteStream;
import io.vertx.grpc.common.GrpcMessageDecoder;
import io.vertx.grpc.common.GrpcMessageEncoder;
import io.vertx.grpc.transcoding.ServiceTranscodingOptions;
import io.vertx.grpc.server.GrpcServerResponse;
import io.vertx.grpc.server.GrpcServer;

Expand All @@ -31,6 +33,14 @@ public class VertxStreamingGrpcServer {
"Source",
GrpcMessageEncoder.json(),
GrpcMessageDecoder.json(() -> examples.Empty.newBuilder()));
public static final ServiceTranscodingOptions Source_TRANSCODING = ServiceTranscodingOptions.create(
"",
HttpMethod.valueOf("POST"),
"/Streaming/Source",
"",
"",
List.of(
));
public static final ServiceMethod<examples.Item, examples.Empty> Sink = ServiceMethod.server(
ServiceName.create("streaming", "Streaming"),
"Sink",
Expand All @@ -41,6 +51,14 @@ public class VertxStreamingGrpcServer {
"Sink",
GrpcMessageEncoder.json(),
GrpcMessageDecoder.json(() -> examples.Item.newBuilder()));
public static final ServiceTranscodingOptions Sink_TRANSCODING = ServiceTranscodingOptions.create(
"",
HttpMethod.valueOf("POST"),
"/Streaming/Sink",
"",
"",
List.of(
));
public static final ServiceMethod<examples.Item, examples.Item> Pipe = ServiceMethod.server(
ServiceName.create("streaming", "Streaming"),
"Pipe",
Expand All @@ -51,6 +69,14 @@ public class VertxStreamingGrpcServer {
"Pipe",
GrpcMessageEncoder.json(),
GrpcMessageDecoder.json(() -> examples.Item.newBuilder()));
public static final ServiceTranscodingOptions Pipe_TRANSCODING = ServiceTranscodingOptions.create(
"",
HttpMethod.valueOf("POST"),
"/Streaming/Pipe",
"",
"",
List.of(
));

public static class StreamingApi {

Expand Down Expand Up @@ -108,6 +134,24 @@ public StreamingApi bind_source(GrpcServer server, io.vertx.grpc.common.WireForm
server.callHandler(serviceMethod, this::handle_source);
return this;
}
public StreamingApi bind_source_with_transcoding(GrpcServer server) {
return bind_source_with_transcoding(server, io.vertx.grpc.common.WireFormat.PROTOBUF);
}
public StreamingApi bind_source_with_transcoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
ServiceMethod<examples.Empty,examples.Item> serviceMethod;
switch(format) {
case PROTOBUF:
serviceMethod = Source;
break;
case JSON:
serviceMethod = Source_JSON;
break;
default:
throw new AssertionError();
}
server.callHandlerWithTranscoding(serviceMethod, this::handle_source, Source_TRANSCODING);
return this;
}
public final void handle_sink(io.vertx.grpc.server.GrpcServerRequest<examples.Item, examples.Empty> request) {
Promise<examples.Empty> promise = Promise.promise();
promise.future()
Expand Down Expand Up @@ -137,6 +181,24 @@ public StreamingApi bind_sink(GrpcServer server, io.vertx.grpc.common.WireFormat
server.callHandler(serviceMethod, this::handle_sink);
return this;
}
public StreamingApi bind_sink_with_transcoding(GrpcServer server) {
return bind_sink_with_transcoding(server, io.vertx.grpc.common.WireFormat.PROTOBUF);
}
public StreamingApi bind_sink_with_transcoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
ServiceMethod<examples.Item,examples.Empty> serviceMethod;
switch(format) {
case PROTOBUF:
serviceMethod = Sink;
break;
case JSON:
serviceMethod = Sink_JSON;
break;
default:
throw new AssertionError();
}
server.callHandlerWithTranscoding(serviceMethod, this::handle_sink, Sink_TRANSCODING);
return this;
}
public final void handle_pipe(io.vertx.grpc.server.GrpcServerRequest<examples.Item, examples.Item> request) {
try {
pipe(request, request.response());
Expand All @@ -162,6 +224,24 @@ public final StreamingApi bind_pipe(GrpcServer server, io.vertx.grpc.common.Wire
server.callHandler(serviceMethod, this::handle_pipe);
return this;
}
public StreamingApi bind_pipe_with_transcoding(GrpcServer server) {
return bind_pipe_with_transcoding(server, io.vertx.grpc.common.WireFormat.PROTOBUF);
}
public StreamingApi bind_pipe_with_transcoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
ServiceMethod<examples.Item,examples.Item> serviceMethod;
switch(format) {
case PROTOBUF:
serviceMethod = Pipe;
break;
case JSON:
serviceMethod = Pipe_JSON;
break;
default:
throw new AssertionError();
}
server.callHandlerWithTranscoding(serviceMethod, this::handle_pipe, Pipe_TRANSCODING);
return this;
}

public final StreamingApi bindAll(GrpcServer server) {
bind_source(server);
Expand All @@ -176,5 +256,19 @@ public final StreamingApi bindAll(GrpcServer server, io.vertx.grpc.common.WireFo
bind_pipe(server, format);
return this;
}

public final StreamingApi bindAllWithTranscoding(GrpcServer server) {
bind_source_with_transcoding(server);
bind_sink_with_transcoding(server);
bind_pipe_with_transcoding(server);
return this;
}

public final StreamingApi bindAllWithTranscoding(GrpcServer server, io.vertx.grpc.common.WireFormat format) {
bind_source_with_transcoding(server, format);
bind_sink_with_transcoding(server, format);
bind_pipe_with_transcoding(server, format);
return this;
}
}
}
67 changes: 67 additions & 0 deletions vertx-grpc-it/src/test/java/io/vertx/grpc/it/TranscodingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.vertx.grpc.it;

import io.grpc.examples.helloworld.VertxGreeterGrpcServer;
import io.vertx.core.Future;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.RequestOptions;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.grpc.server.GrpcServer;
import io.vertx.grpc.server.GrpcServerOptions;
import org.junit.Test;

import java.util.Map;

public class TranscodingTest extends ProxyTestBase {

@Test
public void testUnary01(TestContext should) {
HttpClient client = vertx.createHttpClient();
GrpcServerOptions serverOptions = new GrpcServerOptions().setGrpcTranscodingEnabled(true);

Future<HttpServer> server = vertx.createHttpServer()
.requestHandler(GrpcServer.server(vertx, serverOptions).callHandlerWithTranscoding(VertxGreeterGrpcServer.SayHello_JSON, call -> {
call.handler(helloRequest -> {
io.grpc.examples.helloworld.HelloReply helloReply = io.grpc.examples.helloworld.HelloReply.newBuilder().setMessage("Hello " + helloRequest.getName()).build();
call.response().end(helloReply);
});
}, VertxGreeterGrpcServer.SayHello_TRANSCODING)).listen(8080, "localhost");

RequestOptions options = new RequestOptions().setHost("localhost").setPort(8080).setURI("/Greeter/SayHello").setMethod(HttpMethod.POST);

Async test = should.async();

String data = createRequest("Julien");

server.onComplete(should.asyncAssertSuccess(v -> {
client.request(options).compose(req -> {
req.putHeader("Content-Type", "application/json");
req.putHeader("Accept", "application/json");
req.putHeader("Content-Length", String.valueOf(data.length()));
req.write(data);
return req.send();
}).compose(resp -> {
should.assertEquals(200, resp.statusCode());
should.assertEquals("application/json", resp.getHeader("Content-Type"));
return resp.body();
}).onComplete(should.asyncAssertSuccess(body -> {
should.assertEquals("Hello Julien", getMessage(body.toString()));
test.complete();
}));
}));

test.awaitSuccess(20_000);
}

private String createRequest(String name) {
return Json.encode(new JsonObject().put("name", name));
}

private String getMessage(String message) {
return Json.decodeValue(message, Map.class).get("message").toString();
}
}
31 changes: 31 additions & 0 deletions vertx-grpc-it/src/test/proto/google/api/annotations.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 Google LLC
//
// 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 google.api;

import "google/api/http.proto";
import "google/protobuf/descriptor.proto";

option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";

extend google.protobuf.MethodOptions {
// See `HttpRule`.
HttpRule http = 72295728;
}
Loading

0 comments on commit f4aa5c5

Please sign in to comment.