///|
/// The default cap on a single received gRPC message (4 MiB, matching gRPC's default
/// `MaxRecvMsgSize`). A length prefix above this — including one whose 4 bytes decode
/// to a negative `Int` because the high bit is set — is rejected rather than trusted,
/// so a hostile prefix can neither slice out of bounds nor pin unbounded buffer.
pub let max_message_size : Int = 4 * 1024 * 1024
///|
/// The cap on one accumulated header block (HEADERS plus its CONTINUATION frames).
/// Without it a peer could stream endless non-final CONTINUATION frames and grow the
/// buffer without bound (a CONTINUATION flood); 128 KiB is far above any real gRPC
/// request's headers.
pub let max_header_list_size : Int = 128 * 1024
///|
/// Encode a payload as a gRPC *Length-Prefixed-Message*: a 1-byte compression
/// flag, a 4-byte big-endian length, then the payload. This is the framing every
/// gRPC transport shares (gRPC-Web over HTTP/1.1 and real gRPC over HTTP/2 alike).
pub fn encode_message(payload : Bytes, compressed? : Bool = false) -> Bytes {
let n = payload.length()
let buf = Buffer()
buf.write_byte((if compressed { 1 } else { 0 }).to_byte())
buf.write_byte((n >> 24).to_byte())
buf.write_byte((n >> 16).to_byte())
buf.write_byte((n >> 8).to_byte())
buf.write_byte(n.to_byte())
buf.write_bytes(payload)
buf.to_bytes()
}
///|
/// Decode one gRPC length-prefixed message from the front of `data`, returning
/// `(compressed, payload)`, or `None` if fewer than a full frame is present.
pub fn decode_message(data : Bytes) -> (Bool, Bytes)? {
if data.length() < 5 {
return None
}
let flag = data[0].to_int()
let len = (data[1].to_int() << 24) |
(data[2].to_int() << 16) |
(data[3].to_int() << 8) |
data[4].to_int()
// A high-bit-set prefix decodes to a negative `Int`; compare without recomputing
// `5 + len` (which would wrap) so a hostile length can't slice past the buffer.
if len < 0 || len > data.length() - 5 {
return None
}
Some((flag != 0, data[5:5 + len].to_owned()))
}
///|
/// The 17 canonical gRPC status codes (`grpc-status`).
pub(all) enum Status {
Ok
Cancelled
Unknown
InvalidArgument
DeadlineExceeded
NotFound
AlreadyExists
PermissionDenied
ResourceExhausted
FailedPrecondition
Aborted
OutOfRange
Unimplemented
Internal
Unavailable
DataLoss
Unauthenticated
} derive(Eq)
///|
/// The numeric `grpc-status` code.
pub fn Status::code(self : Status) -> Int {
match self {
Ok => 0
Cancelled => 1
Unknown => 2
InvalidArgument => 3
DeadlineExceeded => 4
NotFound => 5
AlreadyExists => 6
PermissionDenied => 7
ResourceExhausted => 8
FailedPrecondition => 9
Aborted => 10
OutOfRange => 11
Unimplemented => 12
Internal => 13
Unavailable => 14
DataLoss => 15
Unauthenticated => 16
}
}
///|
/// The canonical uppercase status name.
pub fn Status::name(self : Status) -> String {
match self {
Ok => "OK"
Cancelled => "CANCELLED"
Unknown => "UNKNOWN"
InvalidArgument => "INVALID_ARGUMENT"
DeadlineExceeded => "DEADLINE_EXCEEDED"
NotFound => "NOT_FOUND"
AlreadyExists => "ALREADY_EXISTS"
PermissionDenied => "PERMISSION_DENIED"
ResourceExhausted => "RESOURCE_EXHAUSTED"
FailedPrecondition => "FAILED_PRECONDITION"
Aborted => "ABORTED"
OutOfRange => "OUT_OF_RANGE"
Unimplemented => "UNIMPLEMENTED"
Internal => "INTERNAL"
Unavailable => "UNAVAILABLE"
DataLoss => "DATA_LOSS"
Unauthenticated => "UNAUTHENTICATED"
}
}
///|
/// A fully-qualified RPC method: `package.Service` and the method name.
pub(all) struct Method {
service : String
name : String
}
///|
/// The gRPC HTTP/2 `:path`, i.e. `/package.Service/Method`.
pub fn Method::path(self : Method) -> String {
"/" + self.service + "/" + self.name
}