Building a Leak-Safe gRPC Frame Decoder on Reactor Netty
This is the second article in my grpc-reactor series. The first article explains why I chose to build the runtime directly on Reactor Netty and where its compatibility boundary sits. This article moves one layer down into the Stage 1 protocol implementation: the frame decoder that every RPC shape relies on. gRPC protobuf messages are not written directly as raw bytes into HTTP/2 DATA frames. Every message starts with a five-byte envelope: byte 0 bit 0 indicates compression; bits 1-7 must be zero bytes 1-4 unsigned big-endian payload length byte 5..n protobuf message, or its compressed representation Encoding this envelope is straightforward. The difficult part is decoding it without assuming that one input buffer contains one complete frame. HTTP/2, TCP, and Reactor Netty do not promise that buffer boundaries will line up with gRPC message boundaries. This post describes the Stage 1 protocol layer. The project has since progressed beyond it, but the ownership and bounded-decoding rules introduced here remain the foundation for the later transport stages. Encoding Must Define Ownership The contract of GrpcFrameCodec.encode is deliberately explicit: the returned frame and the input message have independent lifetimes. Encoding must not move the input reader index or release the input buffer. The implementation currently copies the readable bytes into a byte array before applying compression: public static ByteBuf encode ( ByteBufAllocator allocator , ByteBuf message , GrpcCompression . Codec compression ) { boolean compressed = ! compression . name (). equals ( "identity" ); byte [] payload = new byte [ message . readableBytes ()]; message . getBytes ( message . readerIndex (), payload ); if ( compressed ) { payload = compression . compress ( payload ); } return allocator . buffer ( GrpcFrameCodec . HEADER_SIZE + payload . length ) . writeByte ( compressed ? 1 : 0 ) . writeInt ( payload . length ) . writeBytes ( payload ); } This is not a zero-copy implementation, and