package body_bytes import ( "fmt" "io" "io/ioutil" "reflect" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" ) // grpc or grpc-gateway does not have an easy way to read the http body into an // annotated body field. so we create a custom marshaller overriding only the // decoder to read the http body into a `bytes` field. based on // - https://rogchap.com/2019/10/19/webhook-endpoint-for-grpc-gateway/ // - https://github.com/grpc-ecosystem/grpc-gateway/issues/652 // // requires `bytes` type for the body field. // // usage: // // custom marshaller for reading whole body into a variable // runtime.WithMarshalerOption( // body_bytes.RawBytesMIME, // &body_bytes.RawBytesPb{JSONPb: &runtime.JSONPb{}}, // ), // // alternatively, use envoy's grpc json transcoder filter, with which you can // set `google.api.HttpBody` type for the annotated body field, and skip all // this. var ( RawBytesMIME = "application/raw-bytes" RawBytesType = reflect.TypeOf([]byte(nil)) ) type RawBytesPb struct { *runtime.JSONPb } func (RawBytesPb) NewDecoder(r io.Reader) runtime.Decoder { return runtime.DecoderFunc(func(v interface{}) error { raw, err := ioutil.ReadAll(r) if err != nil { return err } rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer", v) } rv = rv.Elem() if rv.Type() != RawBytesType { return fmt.Errorf("type must be []byte but got %T", v) } rv.Set(reflect.ValueOf(raw)) return nil }) } func (*RawBytesPb) ContentType(v interface{}) string { return RawBytesMIME }