|
| 1 | +# Helidon Protobuf Support Proposal |
| 2 | + |
| 3 | +Add support for protobuf entities in the Helidon WebServer. |
| 4 | + |
| 5 | +I.e Provide a way to send and receive protobuf messages. |
| 6 | + |
| 7 | +## Examples |
| 8 | + |
| 9 | +### Enable Protobuf Support |
| 10 | + |
| 11 | +The readers and writers for the protobuf entity types will be registered by a service, similar to how other media types are supported in the webserver. All the examples below assume that the ProtobufSupport is registered as follow: |
| 12 | + |
| 13 | +```java |
| 14 | +Routing.builder() |
| 15 | + .register(ProtobufSupport.create(GreetingProtos.getDescriptor())) |
| 16 | + // user defined handlers... |
| 17 | + .build()); |
| 18 | +``` |
| 19 | + |
| 20 | +The ProtobufSupport builder accepts an optional FileDescriptor instance that will be used to lookup the parsers. If not supplied by the user, the parsers will be looked up using reflection and cached for subsequent requests. |
| 21 | + |
| 22 | +### Process a request payload |
| 23 | + |
| 24 | +```java |
| 25 | +request.content().as(GreetingProtos.Greeting.class).thenAccept((greeting) -> { |
| 26 | + response.send("received" + greeting.getGreeting()); |
| 27 | +}); |
| 28 | +``` |
| 29 | + |
| 30 | +### Return a response |
| 31 | + |
| 32 | +```java |
| 33 | +response.send(GreetingProtos.Greeting.newBuilder() |
| 34 | + .setGreeting("Hello World!") |
| 35 | + .build()); |
| 36 | +``` |
| 37 | + |
| 38 | +## About protobuf |
| 39 | + |
| 40 | +Protobuf is basically a serialization framework with a toolchain to generate code, it is agnostic of transport protocol. |
| 41 | +One can serialize to and deserialize from bytes. See https://developers.google.com/protocol-buffers |
| 42 | + |
| 43 | +Data structures are defined in a `.proto` file using the protocol buffers langague. See https://developers.google.com/protocol-buffers/docs/proto3 |
| 44 | + |
| 45 | +Code is generated by running the protocol buffer compiler `protoc`. This compiler not written in java but instead is compiled to native code ; executables are provided for all major platforms. A Maven plugin is available (`org.xolstice.maven.plugins:protobuf-maven-plugin`), it integrates `protoc` with Maven projects. |
| 46 | + |
| 47 | +The generated code can be used to serialize and deserialize protobuf data types. It also provide a reflection mechanism to introspect the data types. |
| 48 | + |
| 49 | +The protobuf compiler has a plugin interface that can be used to generate additional code. It is langage agnostic and uses the standard input and standard output. A code generation request is sent in the standard input as serialized bytes, the code generation response is sent into the standard output as serialized bytes ; a plugin uses the provided code for the request and resposne object to deserialize the request, build and serialize a response. |
| 50 | + |
| 51 | +Any type declared in a `.proto` file will have a matching generated java class implementing `com.google.protobuf.Descriptors.Descriptors`. The `.proto` file also has a matching generated java class implementing `com.google.protobuf.Descriptors.FileDescriptor`. This class is the main can be used as the main "reflection" entrypoint. |
| 52 | + |
| 53 | +Any protobuf object instance is a message, represented in java by the type `com.google.protobuf.Message`. |
| 54 | + |
| 55 | +There are different level of messages: |
| 56 | + - `com.google.protobuf.Message` extends `com.google.protobuf.MessageLite` |
| 57 | + - `com.google.protobuf.MessageLite` |
| 58 | + |
| 59 | +The main difference is that there is no "reflection" methods available from `MessageLite`. |
| 60 | + |
| 61 | +## Another Media Type |
| 62 | + |
| 63 | +Protobuf is transport protocol agnostic. We can support it with either HTTP1 or HTTP2. |
| 64 | + |
| 65 | +There is no standard content-type header for protobuf, however there are some popular ones that are commonly used. |
| 66 | + |
| 67 | +The popular headers are as follow: |
| 68 | + |
| 69 | +- `application/x-protobuf ; messageType=x.y.Z` |
| 70 | +- `application/x-protobuffer ; messageType=x.y.Z` |
| 71 | + |
| 72 | +These headers can carry the message type as parameters. `application/octet-stream` is sometimes used as well with protobuf however it only describes a binary stream content and does not carry the message type information. |
| 73 | + |
| 74 | +## Receiving Protobuf messages |
| 75 | + |
| 76 | +Deserializing a message requires having a handle on a parser for the type that matches that message. If the message type can be determined from the request headers and if the user has provided a `FileDescriptor`, the parser can be looked-up dynamically. |
| 77 | + |
| 78 | +If the message type is not available, the parser can be looked-up using reflection: invoke the `.parser()` static method on the requested class. |
| 79 | + |
| 80 | +## Sending Protobuf messages |
| 81 | + |
| 82 | +Sending protobuf message is easier given that the user provides an instance of a message and that the base type `MessageLite` provides serialization messages (`message.toByteArray()`). |
| 83 | + |
| 84 | +## Netty Support |
| 85 | + |
| 86 | +Since protobuf is transport agnostic there isn't much needed from Netty. However things get a little complicated because of protobuf's `Base 128 varints`, see https://developers.google.com/protocol-buffers/docs/encoding#varints |
| 87 | + |
| 88 | +Netty provides ways to deal with that: |
| 89 | +- https://github.com/netty/netty/blob/netty-4.1.30.Final/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java |
| 90 | +- https://github.com/netty/netty/blob/netty-4.1.30.Final/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java |
| 91 | + |
| 92 | +## RPC |
| 93 | + |
| 94 | +Protobuf defines service with RPC methods, and google provides some special `.proto` includes providing HTTP annotations. |
| 95 | + |
| 96 | +These 2 things while in the protobuf space are strongly associated with GRPC. From the perspective of protobuf GRPC is one implementation of a toolchain for RPC services that consists of a `protoc` plugin and a stack that supports various langagues. |
| 97 | + |
| 98 | +It would it be possible for us to define our own "Helidon" flavor of protobuf RPC services, however they wouldn't interoperate with GRPC generated code ; given the popularity of GRPC the usability of such RPC services is questionnable. |
| 99 | + |
| 100 | +Support for the RPC and interoperability with GRPC clients should be done separately from the basic protobuf support. |
| 101 | + |
| 102 | +## Open Issues |
| 103 | + |
| 104 | +- Do we need to use `ProtobufVarint32FrameDecoder` and `ProtobufVarint32LengthFieldPrepender` and would that impact other non protobuf message. If yes, then how do we deal with it ? Do we "multiplex" our ForwardingHandler ? |
| 105 | + |
0 commit comments