1*1c12ee1eSDan Willemsen// Copyright 2022 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// Package protodelim marshals and unmarshals varint size-delimited messages. 6*1c12ee1eSDan Willemsenpackage protodelim 7*1c12ee1eSDan Willemsen 8*1c12ee1eSDan Willemsenimport ( 9*1c12ee1eSDan Willemsen "encoding/binary" 10*1c12ee1eSDan Willemsen "fmt" 11*1c12ee1eSDan Willemsen "io" 12*1c12ee1eSDan Willemsen 13*1c12ee1eSDan Willemsen "google.golang.org/protobuf/encoding/protowire" 14*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/errors" 15*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 16*1c12ee1eSDan Willemsen) 17*1c12ee1eSDan Willemsen 18*1c12ee1eSDan Willemsen// MarshalOptions is a configurable varint size-delimited marshaler. 19*1c12ee1eSDan Willemsentype MarshalOptions struct{ proto.MarshalOptions } 20*1c12ee1eSDan Willemsen 21*1c12ee1eSDan Willemsen// MarshalTo writes a varint size-delimited wire-format message to w. 22*1c12ee1eSDan Willemsen// If w returns an error, MarshalTo returns it unchanged. 23*1c12ee1eSDan Willemsenfunc (o MarshalOptions) MarshalTo(w io.Writer, m proto.Message) (int, error) { 24*1c12ee1eSDan Willemsen msgBytes, err := o.MarshalOptions.Marshal(m) 25*1c12ee1eSDan Willemsen if err != nil { 26*1c12ee1eSDan Willemsen return 0, err 27*1c12ee1eSDan Willemsen } 28*1c12ee1eSDan Willemsen 29*1c12ee1eSDan Willemsen sizeBytes := protowire.AppendVarint(nil, uint64(len(msgBytes))) 30*1c12ee1eSDan Willemsen sizeWritten, err := w.Write(sizeBytes) 31*1c12ee1eSDan Willemsen if err != nil { 32*1c12ee1eSDan Willemsen return sizeWritten, err 33*1c12ee1eSDan Willemsen } 34*1c12ee1eSDan Willemsen msgWritten, err := w.Write(msgBytes) 35*1c12ee1eSDan Willemsen if err != nil { 36*1c12ee1eSDan Willemsen return sizeWritten + msgWritten, err 37*1c12ee1eSDan Willemsen } 38*1c12ee1eSDan Willemsen return sizeWritten + msgWritten, nil 39*1c12ee1eSDan Willemsen} 40*1c12ee1eSDan Willemsen 41*1c12ee1eSDan Willemsen// MarshalTo writes a varint size-delimited wire-format message to w 42*1c12ee1eSDan Willemsen// with the default options. 43*1c12ee1eSDan Willemsen// 44*1c12ee1eSDan Willemsen// See the documentation for MarshalOptions.MarshalTo. 45*1c12ee1eSDan Willemsenfunc MarshalTo(w io.Writer, m proto.Message) (int, error) { 46*1c12ee1eSDan Willemsen return MarshalOptions{}.MarshalTo(w, m) 47*1c12ee1eSDan Willemsen} 48*1c12ee1eSDan Willemsen 49*1c12ee1eSDan Willemsen// UnmarshalOptions is a configurable varint size-delimited unmarshaler. 50*1c12ee1eSDan Willemsentype UnmarshalOptions struct { 51*1c12ee1eSDan Willemsen proto.UnmarshalOptions 52*1c12ee1eSDan Willemsen 53*1c12ee1eSDan Willemsen // MaxSize is the maximum size in wire-format bytes of a single message. 54*1c12ee1eSDan Willemsen // Unmarshaling a message larger than MaxSize will return an error. 55*1c12ee1eSDan Willemsen // A zero MaxSize will default to 4 MiB. 56*1c12ee1eSDan Willemsen // Setting MaxSize to -1 disables the limit. 57*1c12ee1eSDan Willemsen MaxSize int64 58*1c12ee1eSDan Willemsen} 59*1c12ee1eSDan Willemsen 60*1c12ee1eSDan Willemsenconst defaultMaxSize = 4 << 20 // 4 MiB, corresponds to the default gRPC max request/response size 61*1c12ee1eSDan Willemsen 62*1c12ee1eSDan Willemsen// SizeTooLargeError is an error that is returned when the unmarshaler encounters a message size 63*1c12ee1eSDan Willemsen// that is larger than its configured MaxSize. 64*1c12ee1eSDan Willemsentype SizeTooLargeError struct { 65*1c12ee1eSDan Willemsen // Size is the varint size of the message encountered 66*1c12ee1eSDan Willemsen // that was larger than the provided MaxSize. 67*1c12ee1eSDan Willemsen Size uint64 68*1c12ee1eSDan Willemsen 69*1c12ee1eSDan Willemsen // MaxSize is the MaxSize limit configured in UnmarshalOptions, which Size exceeded. 70*1c12ee1eSDan Willemsen MaxSize uint64 71*1c12ee1eSDan Willemsen} 72*1c12ee1eSDan Willemsen 73*1c12ee1eSDan Willemsenfunc (e *SizeTooLargeError) Error() string { 74*1c12ee1eSDan Willemsen return fmt.Sprintf("message size %d exceeded unmarshaler's maximum configured size %d", e.Size, e.MaxSize) 75*1c12ee1eSDan Willemsen} 76*1c12ee1eSDan Willemsen 77*1c12ee1eSDan Willemsen// Reader is the interface expected by UnmarshalFrom. 78*1c12ee1eSDan Willemsen// It is implemented by *bufio.Reader. 79*1c12ee1eSDan Willemsentype Reader interface { 80*1c12ee1eSDan Willemsen io.Reader 81*1c12ee1eSDan Willemsen io.ByteReader 82*1c12ee1eSDan Willemsen} 83*1c12ee1eSDan Willemsen 84*1c12ee1eSDan Willemsen// UnmarshalFrom parses and consumes a varint size-delimited wire-format message 85*1c12ee1eSDan Willemsen// from r. 86*1c12ee1eSDan Willemsen// The provided message must be mutable (e.g., a non-nil pointer to a message). 87*1c12ee1eSDan Willemsen// 88*1c12ee1eSDan Willemsen// The error is io.EOF error only if no bytes are read. 89*1c12ee1eSDan Willemsen// If an EOF happens after reading some but not all the bytes, 90*1c12ee1eSDan Willemsen// UnmarshalFrom returns a non-io.EOF error. 91*1c12ee1eSDan Willemsen// In particular if r returns a non-io.EOF error, UnmarshalFrom returns it unchanged, 92*1c12ee1eSDan Willemsen// and if only a size is read with no subsequent message, io.ErrUnexpectedEOF is returned. 93*1c12ee1eSDan Willemsenfunc (o UnmarshalOptions) UnmarshalFrom(r Reader, m proto.Message) error { 94*1c12ee1eSDan Willemsen var sizeArr [binary.MaxVarintLen64]byte 95*1c12ee1eSDan Willemsen sizeBuf := sizeArr[:0] 96*1c12ee1eSDan Willemsen for i := range sizeArr { 97*1c12ee1eSDan Willemsen b, err := r.ReadByte() 98*1c12ee1eSDan Willemsen if err != nil && (err != io.EOF || i == 0) { 99*1c12ee1eSDan Willemsen return err 100*1c12ee1eSDan Willemsen } 101*1c12ee1eSDan Willemsen sizeBuf = append(sizeBuf, b) 102*1c12ee1eSDan Willemsen if b < 0x80 { 103*1c12ee1eSDan Willemsen break 104*1c12ee1eSDan Willemsen } 105*1c12ee1eSDan Willemsen } 106*1c12ee1eSDan Willemsen size, n := protowire.ConsumeVarint(sizeBuf) 107*1c12ee1eSDan Willemsen if n < 0 { 108*1c12ee1eSDan Willemsen return protowire.ParseError(n) 109*1c12ee1eSDan Willemsen } 110*1c12ee1eSDan Willemsen 111*1c12ee1eSDan Willemsen maxSize := o.MaxSize 112*1c12ee1eSDan Willemsen if maxSize == 0 { 113*1c12ee1eSDan Willemsen maxSize = defaultMaxSize 114*1c12ee1eSDan Willemsen } 115*1c12ee1eSDan Willemsen if maxSize != -1 && size > uint64(maxSize) { 116*1c12ee1eSDan Willemsen return errors.Wrap(&SizeTooLargeError{Size: size, MaxSize: uint64(maxSize)}, "") 117*1c12ee1eSDan Willemsen } 118*1c12ee1eSDan Willemsen 119*1c12ee1eSDan Willemsen b := make([]byte, size) 120*1c12ee1eSDan Willemsen _, err := io.ReadFull(r, b) 121*1c12ee1eSDan Willemsen if err == io.EOF { 122*1c12ee1eSDan Willemsen return io.ErrUnexpectedEOF 123*1c12ee1eSDan Willemsen } 124*1c12ee1eSDan Willemsen if err != nil { 125*1c12ee1eSDan Willemsen return err 126*1c12ee1eSDan Willemsen } 127*1c12ee1eSDan Willemsen if err := o.Unmarshal(b, m); err != nil { 128*1c12ee1eSDan Willemsen return err 129*1c12ee1eSDan Willemsen } 130*1c12ee1eSDan Willemsen return nil 131*1c12ee1eSDan Willemsen} 132*1c12ee1eSDan Willemsen 133*1c12ee1eSDan Willemsen// UnmarshalFrom parses and consumes a varint size-delimited wire-format message 134*1c12ee1eSDan Willemsen// from r with the default options. 135*1c12ee1eSDan Willemsen// The provided message must be mutable (e.g., a non-nil pointer to a message). 136*1c12ee1eSDan Willemsen// 137*1c12ee1eSDan Willemsen// See the documentation for UnmarshalOptions.UnmarshalFrom. 138*1c12ee1eSDan Willemsenfunc UnmarshalFrom(r Reader, m proto.Message) error { 139*1c12ee1eSDan Willemsen return UnmarshalOptions{}.UnmarshalFrom(r, m) 140*1c12ee1eSDan Willemsen} 141