Skip to content

grpc

stable

Encode and decode Protocol Buffer wire format fields and gRPC frames, and build gRPC HTTP/2 request metadata.

use plugin grpc::{encode_varint, decode_varint, encode_string_field, …}
19 functions Networking
/ filter jk navigate Esc clear
Functions (19)
  1. encode_varint Encode integer as protobuf LEB128 varint bytes
  2. decode_varint Decode varint bytes to value and bytes consumed
  3. encode_string_field Encode a protobuf string field
  4. encode_int_field Encode a protobuf integer field
  5. decode_fields Decode all fields from a protobuf message
  6. encode_grpc_frame Wrap message bytes in a gRPC length-prefixed frame
  7. decode_grpc_frame Unwrap a gRPC frame into header and message
  8. build_grpc_request Build gRPC HTTP/2 request metadata and body
  9. parse_grpc_status Extract status code and message from response headers
  10. status_name Map a gRPC status code to its canonical name
  11. encode_bytes_field Encode a protobuf bytes/embedded-message field
  12. encode_map Encode protobuf map entries
  13. decode_map Decode protobuf map entries
  14. encode_fixed32_field Encode a 32-bit fixed-width field
  15. encode_fixed64_field Encode a 64-bit fixed-width field
  16. concat_bytes Concatenate two byte sequences
  17. encode_float_field Encode a protobuf float field
  18. encode_double_field Encode a protobuf double field
  19. encode_bool_field Encode a protobuf bool field

Overview

grpc is a low-level toolkit for the Protocol Buffers wire format and the gRPC framing layer, built entirely on plain byte sequences. It has no client of its own and opens no sockets: every function either produces bytes (the encoded form of a protobuf field, a map, or a gRPC frame) or consumes bytes and returns a decoded table. Because each encoder emits a self-contained chunk for a single field, you assemble a full message by concatenating those chunks with concat_bytes, wrap the result in a gRPC length-prefixed frame, and pair it with the HTTP/2 metadata from build_grpc_request.

The mental model is bottom-up: encode individual fields (encode_string_field, encode_int_field, the encode_*_field family), glue them together, frame the message with encode_grpc_frame, send it over your own HTTP/2 transport, then read the reply back with decode_grpc_frame, decode_fields, and parse_grpc_status / status_name. Reach for it whenever you need to speak gRPC or read and write protobuf bytes without depending on generated stubs.

Common patterns

Build a multi-field protobuf message and wrap it in a gRPC request:

use plugin grpc::{encode_string_field, encode_int_field, concat_bytes, build_grpc_request}

let body = concat_bytes(encode_string_field(1, "Alice"), encode_int_field(2, 30))
let req = build_grpc_request("user.UserService", "CreateUser", body)
print("POST {req["path"]} ({req["content_type"]})")

Frame a message, then decode the frame and its fields on the other side:

use plugin grpc::{encode_string_field, encode_grpc_frame, decode_grpc_frame, decode_fields}

let frame = encode_grpc_frame(encode_string_field(1, "ping"), false)
let unwrapped = decode_grpc_frame(frame)
let fields = decode_fields(unwrapped["message"])
print("field {fields[0]["field_number"]} is wire type {fields[0]["wire_type"]}")

Inspect a gRPC status returned in response trailers:

use plugin grpc::{parse_grpc_status, status_name}

let trailers = #{"grpc-status": "5", "grpc-message": "user not found"}
let status = parse_grpc_status(trailers)
print("{status_name(status["code"])}: {status["message"]}")

Encode integer as protobuf LEB128 varint bytes

Encodes a non-negative integer using protobuf LEB128 variable-length encoding.

use plugin grpc::{encode_varint}

let b = encode_varint(300)
print(b.len())

Decode varint bytes to value and bytes consumed

Decodes the first varint from bytes and returns a table with value (int) and bytes_read (int).

use plugin grpc::{encode_varint, decode_varint}

let b = encode_varint(300)
let result = decode_varint(b)
print(result["value"])
print(result["bytes_read"])

Encode a protobuf string field

Encodes a string as a length-delimited protobuf field (wire type 2) for the given field number.

use plugin grpc::{encode_string_field}

let b = encode_string_field(1, "hello")
print(b.len())

Encode a protobuf integer field

Encodes an integer as a varint protobuf field (wire type 0).

use plugin grpc::{encode_int_field}

let b = encode_int_field(2, 42)
print(b.len())

Decode all fields from a protobuf message

Decodes all fields from a protobuf-encoded message. Returns a list of {field_number, wire_type, data} entries.

use plugin grpc::{encode_string_field, encode_int_field, concat_bytes, decode_fields}

let msg = concat_bytes(encode_string_field(1, "Alice"), encode_int_field(2, 30))
let fields = decode_fields(msg)
print(fields[0]["field_number"])

Iterate every decoded field to print its wire type alongside its number:

use plugin grpc::{encode_string_field, encode_int_field, encode_bool_field, concat_bytes, decode_fields}

let msg = concat_bytes(concat_bytes(encode_string_field(1, "Bob"), encode_int_field(2, 7)), encode_bool_field(3, true))
let fields = decode_fields(msg)
for field in fields {
  print("#{field["field_number"]} wire={field["wire_type"]}")
}

Wrap message bytes in a gRPC length-prefixed frame

Wraps message bytes in a gRPC 5-byte length-prefixed frame: [compressed:1][length:4][message:N].

use plugin grpc::{encode_string_field, encode_grpc_frame, decode_grpc_frame}

let msg = encode_string_field(1, "ping")
let frame = encode_grpc_frame(msg, false)
let decoded = decode_grpc_frame(frame)
print(decoded["compressed"])
print(decoded["length"])

Unwrap a gRPC frame into header and message

Parses a gRPC frame and returns {compressed, length, message}. Errors if the input is shorter than five bytes or is truncated relative to its declared length.

use plugin grpc::{encode_int_field, encode_grpc_frame, decode_grpc_frame}

let frame = encode_grpc_frame(encode_int_field(1, 99), false)
let result = decode_grpc_frame(frame)
print(result["length"])

Build gRPC HTTP/2 request metadata and body

Builds the HTTP/2 metadata needed for a gRPC call: path, content_type, te, grpc_encoding, and body (the message already wrapped in an uncompressed frame).

use plugin grpc::{encode_string_field, build_grpc_request}

let msg = encode_string_field(1, "world")
let req = build_grpc_request("helloworld.Greeter", "SayHello", msg)
print(req["path"])
print(req["content_type"])

Extract status code and message from response headers

Reads grpc-status, grpc-message, and grpc-status-details-bin from a response headers table (keys are matched case-insensitively). Returns {code, message, details}, with code defaulting to -1 when no status header is present.

use plugin grpc::{parse_grpc_status, status_name}

let headers = #{"grpc-status": "0", "grpc-message": "OK"}
let status = parse_grpc_status(headers)
print(status_name(status["code"]))

Map a gRPC status code to its canonical name

Maps a gRPC status integer code (0–16) to its canonical name such as "OK", "NOT_FOUND", or "UNAUTHENTICATED". Unknown codes return "UNKNOWN".

use plugin grpc::{status_name}

print(status_name(5))
print(status_name(0))

Encode a protobuf bytes/embedded-message field

Encodes a byte sequence as a length-delimited protobuf field (wire type 2). Use this for bytes fields and for nesting an already-encoded embedded message.

use plugin grpc::{encode_string_field, encode_bytes_field, decode_fields}

let inner = encode_string_field(1, "nested")
let outer = encode_bytes_field(5, inner)
let fields = decode_fields(outer)
print(fields[0]["field_number"])

Encode protobuf map entries

Encodes a table of (key, value) pairs as a sequence of protobuf map entries. Each entry becomes a length-delimited message with field 1 as the key and field 2 as the value; string, integer, and bytes values are supported.

use plugin grpc::{encode_map, decode_map}

let encoded = encode_map(#{"name": "Alice", "city": "Berlin"})
let back = decode_map(encoded, "string", "string")
print(back["name"])

Decode protobuf map entries

Decodes a sequence of protobuf map entries back into a table. key_type and value_type select how each side is interpreted: "string", "int", or "bytes".

use plugin grpc::{encode_map, decode_map}

let encoded = encode_map(#{"retries": 3, "timeout": 30})
let settings = decode_map(encoded, "string", "int")
print(settings["timeout"])

Encode a 32-bit fixed-width field

Encodes an integer as a protobuf fixed32 field (wire type 5, four little-endian bytes). Unlike a varint, the width is constant regardless of the value.

use plugin grpc::{encode_fixed32_field}

let b = encode_fixed32_field(4, 1000)
print(b.len())

Encode a 64-bit fixed-width field

Encodes an integer as a protobuf fixed64 field (wire type 1, eight little-endian bytes).

use plugin grpc::{encode_fixed64_field}

let b = encode_fixed64_field(6, 9000000000)
print(b.len())

Concatenate two byte sequences

Concatenates two byte sequences. Useful for assembling multi-field protobuf messages before framing.

use plugin grpc::{encode_string_field, encode_int_field, concat_bytes}

let name_field = encode_string_field(1, "Alice")
let age_field = encode_int_field(2, 30)
let msg = concat_bytes(name_field, age_field)
print(msg.len())

Encode a protobuf float field

Encodes a number as a protobuf float field (wire type 5, 32-bit IEEE-754). The value is narrowed to single precision.

use plugin grpc::{encode_float_field}

let b = encode_float_field(7, 3.14)
print(b.len())

Encode a protobuf double field

Encodes a number as a protobuf double field (wire type 1, 64-bit IEEE-754), preserving full double precision.

use plugin grpc::{encode_double_field, concat_bytes, encode_string_field, encode_grpc_frame}

let payload = concat_bytes(encode_string_field(1, "EUR"), encode_double_field(2, 19.99))
let frame = encode_grpc_frame(payload, false)
print(frame.len())

Encode a protobuf bool field

Encodes a boolean as a varint protobuf field (0 for false, 1 for true).

use plugin grpc::{encode_bool_field}

let b = encode_bool_field(3, true)
print(b.len())
enespt-br