123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- package runtime
- import (
- "encoding/base64"
- "fmt"
- "net/url"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "time"
- "github.com/golang/protobuf/proto"
- "github.com/grpc-ecosystem/grpc-gateway/utilities"
- "google.golang.org/grpc/grpclog"
- )
- // PopulateQueryParameters populates "values" into "msg".
- // A value is ignored if its key starts with one of the elements in "filter".
- func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
- for key, values := range values {
- re, err := regexp.Compile("^(.*)\\[(.*)\\]$")
- if err != nil {
- return err
- }
- match := re.FindStringSubmatch(key)
- if len(match) == 3 {
- key = match[1]
- values = append([]string{match[2]}, values...)
- }
- fieldPath := strings.Split(key, ".")
- if filter.HasCommonPrefix(fieldPath) {
- continue
- }
- if err := populateFieldValueFromPath(msg, fieldPath, values); err != nil {
- return err
- }
- }
- return nil
- }
- // PopulateFieldFromPath sets a value in a nested Protobuf structure.
- // It instantiates missing protobuf fields as it goes.
- func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error {
- fieldPath := strings.Split(fieldPathString, ".")
- return populateFieldValueFromPath(msg, fieldPath, []string{value})
- }
- func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []string) error {
- m := reflect.ValueOf(msg)
- if m.Kind() != reflect.Ptr {
- return fmt.Errorf("unexpected type %T: %v", msg, msg)
- }
- var props *proto.Properties
- m = m.Elem()
- for i, fieldName := range fieldPath {
- isLast := i == len(fieldPath)-1
- if !isLast && m.Kind() != reflect.Struct {
- return fmt.Errorf("non-aggregate type in the mid of path: %s", strings.Join(fieldPath, "."))
- }
- var f reflect.Value
- var err error
- f, props, err = fieldByProtoName(m, fieldName)
- if err != nil {
- return err
- } else if !f.IsValid() {
- grpclog.Infof("field not found in %T: %s", msg, strings.Join(fieldPath, "."))
- return nil
- }
- switch f.Kind() {
- case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64:
- if !isLast {
- return fmt.Errorf("unexpected nested field %s in %s", fieldPath[i+1], strings.Join(fieldPath[:i+1], "."))
- }
- m = f
- case reflect.Slice:
- if !isLast {
- return fmt.Errorf("unexpected repeated field in %s", strings.Join(fieldPath, "."))
- }
- // Handle []byte
- if f.Type().Elem().Kind() == reflect.Uint8 {
- m = f
- break
- }
- return populateRepeatedField(f, values, props)
- case reflect.Ptr:
- if f.IsNil() {
- m = reflect.New(f.Type().Elem())
- f.Set(m.Convert(f.Type()))
- }
- m = f.Elem()
- continue
- case reflect.Struct:
- m = f
- continue
- case reflect.Map:
- if !isLast {
- return fmt.Errorf("unexpected nested field %s in %s", fieldPath[i+1], strings.Join(fieldPath[:i+1], "."))
- }
- return populateMapField(f, values, props)
- default:
- return fmt.Errorf("unexpected type %s in %T", f.Type(), msg)
- }
- }
- switch len(values) {
- case 0:
- return fmt.Errorf("no value of field: %s", strings.Join(fieldPath, "."))
- case 1:
- default:
- grpclog.Infof("too many field values: %s", strings.Join(fieldPath, "."))
- }
- return populateField(m, values[0], props)
- }
- // fieldByProtoName looks up a field whose corresponding protobuf field name is "name".
- // "m" must be a struct value. It returns zero reflect.Value if no such field found.
- func fieldByProtoName(m reflect.Value, name string) (reflect.Value, *proto.Properties, error) {
- props := proto.GetProperties(m.Type())
- // look up field name in oneof map
- if op, ok := props.OneofTypes[name]; ok {
- v := reflect.New(op.Type.Elem())
- field := m.Field(op.Field)
- if !field.IsNil() {
- return reflect.Value{}, nil, fmt.Errorf("field already set for %s oneof", props.Prop[op.Field].OrigName)
- }
- field.Set(v)
- return v.Elem().Field(0), op.Prop, nil
- }
- for _, p := range props.Prop {
- if p.OrigName == name {
- return m.FieldByName(p.Name), p, nil
- }
- if p.JSONName == name {
- return m.FieldByName(p.Name), p, nil
- }
- }
- return reflect.Value{}, nil, nil
- }
- func populateMapField(f reflect.Value, values []string, props *proto.Properties) error {
- if len(values) != 2 {
- return fmt.Errorf("more than one value provided for key %s in map %s", values[0], props.Name)
- }
- key, value := values[0], values[1]
- keyType := f.Type().Key()
- valueType := f.Type().Elem()
- if f.IsNil() {
- f.Set(reflect.MakeMap(f.Type()))
- }
- keyConv, ok := convFromType[keyType.Kind()]
- if !ok {
- return fmt.Errorf("unsupported key type %s in map %s", keyType, props.Name)
- }
- valueConv, ok := convFromType[valueType.Kind()]
- if !ok {
- return fmt.Errorf("unsupported value type %s in map %s", valueType, props.Name)
- }
- keyV := keyConv.Call([]reflect.Value{reflect.ValueOf(key)})
- if err := keyV[1].Interface(); err != nil {
- return err.(error)
- }
- valueV := valueConv.Call([]reflect.Value{reflect.ValueOf(value)})
- if err := valueV[1].Interface(); err != nil {
- return err.(error)
- }
- f.SetMapIndex(keyV[0].Convert(keyType), valueV[0].Convert(valueType))
- return nil
- }
- func populateRepeatedField(f reflect.Value, values []string, props *proto.Properties) error {
- elemType := f.Type().Elem()
- // is the destination field a slice of an enumeration type?
- if enumValMap := proto.EnumValueMap(props.Enum); enumValMap != nil {
- return populateFieldEnumRepeated(f, values, enumValMap)
- }
- conv, ok := convFromType[elemType.Kind()]
- if !ok {
- return fmt.Errorf("unsupported field type %s", elemType)
- }
- f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)).Convert(f.Type()))
- for i, v := range values {
- result := conv.Call([]reflect.Value{reflect.ValueOf(v)})
- if err := result[1].Interface(); err != nil {
- return err.(error)
- }
- f.Index(i).Set(result[0].Convert(f.Index(i).Type()))
- }
- return nil
- }
- func populateField(f reflect.Value, value string, props *proto.Properties) error {
- i := f.Addr().Interface()
- // Handle protobuf well known types
- var name string
- switch m := i.(type) {
- case interface{ XXX_WellKnownType() string }:
- name = m.XXX_WellKnownType()
- case proto.Message:
- const wktPrefix = "google.protobuf."
- if fullName := proto.MessageName(m); strings.HasPrefix(fullName, wktPrefix) {
- name = fullName[len(wktPrefix):]
- }
- }
- switch name {
- case "Timestamp":
- if value == "null" {
- f.FieldByName("Seconds").SetInt(0)
- f.FieldByName("Nanos").SetInt(0)
- return nil
- }
- t, err := time.Parse(time.RFC3339Nano, value)
- if err != nil {
- return fmt.Errorf("bad Timestamp: %v", err)
- }
- f.FieldByName("Seconds").SetInt(int64(t.Unix()))
- f.FieldByName("Nanos").SetInt(int64(t.Nanosecond()))
- return nil
- case "Duration":
- if value == "null" {
- f.FieldByName("Seconds").SetInt(0)
- f.FieldByName("Nanos").SetInt(0)
- return nil
- }
- d, err := time.ParseDuration(value)
- if err != nil {
- return fmt.Errorf("bad Duration: %v", err)
- }
- ns := d.Nanoseconds()
- s := ns / 1e9
- ns %= 1e9
- f.FieldByName("Seconds").SetInt(s)
- f.FieldByName("Nanos").SetInt(ns)
- return nil
- case "DoubleValue":
- fallthrough
- case "FloatValue":
- float64Val, err := strconv.ParseFloat(value, 64)
- if err != nil {
- return fmt.Errorf("bad DoubleValue: %s", value)
- }
- f.FieldByName("Value").SetFloat(float64Val)
- return nil
- case "Int64Value":
- fallthrough
- case "Int32Value":
- int64Val, err := strconv.ParseInt(value, 10, 64)
- if err != nil {
- return fmt.Errorf("bad DoubleValue: %s", value)
- }
- f.FieldByName("Value").SetInt(int64Val)
- return nil
- case "UInt64Value":
- fallthrough
- case "UInt32Value":
- uint64Val, err := strconv.ParseUint(value, 10, 64)
- if err != nil {
- return fmt.Errorf("bad DoubleValue: %s", value)
- }
- f.FieldByName("Value").SetUint(uint64Val)
- return nil
- case "BoolValue":
- if value == "true" {
- f.FieldByName("Value").SetBool(true)
- } else if value == "false" {
- f.FieldByName("Value").SetBool(false)
- } else {
- return fmt.Errorf("bad BoolValue: %s", value)
- }
- return nil
- case "StringValue":
- f.FieldByName("Value").SetString(value)
- return nil
- case "BytesValue":
- bytesVal, err := base64.StdEncoding.DecodeString(value)
- if err != nil {
- return fmt.Errorf("bad BytesValue: %s", value)
- }
- f.FieldByName("Value").SetBytes(bytesVal)
- return nil
- case "FieldMask":
- p := f.FieldByName("Paths")
- for _, v := range strings.Split(value, ",") {
- if v != "" {
- p.Set(reflect.Append(p, reflect.ValueOf(v)))
- }
- }
- return nil
- }
- // Handle Time and Duration stdlib types
- switch t := i.(type) {
- case *time.Time:
- pt, err := time.Parse(time.RFC3339Nano, value)
- if err != nil {
- return fmt.Errorf("bad Timestamp: %v", err)
- }
- *t = pt
- return nil
- case *time.Duration:
- d, err := time.ParseDuration(value)
- if err != nil {
- return fmt.Errorf("bad Duration: %v", err)
- }
- *t = d
- return nil
- }
- // is the destination field an enumeration type?
- if enumValMap := proto.EnumValueMap(props.Enum); enumValMap != nil {
- return populateFieldEnum(f, value, enumValMap)
- }
- conv, ok := convFromType[f.Kind()]
- if !ok {
- return fmt.Errorf("field type %T is not supported in query parameters", i)
- }
- result := conv.Call([]reflect.Value{reflect.ValueOf(value)})
- if err := result[1].Interface(); err != nil {
- return err.(error)
- }
- f.Set(result[0].Convert(f.Type()))
- return nil
- }
- func convertEnum(value string, t reflect.Type, enumValMap map[string]int32) (reflect.Value, error) {
- // see if it's an enumeration string
- if enumVal, ok := enumValMap[value]; ok {
- return reflect.ValueOf(enumVal).Convert(t), nil
- }
- // check for an integer that matches an enumeration value
- eVal, err := strconv.Atoi(value)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("%s is not a valid %s", value, t)
- }
- for _, v := range enumValMap {
- if v == int32(eVal) {
- return reflect.ValueOf(eVal).Convert(t), nil
- }
- }
- return reflect.Value{}, fmt.Errorf("%s is not a valid %s", value, t)
- }
- func populateFieldEnum(f reflect.Value, value string, enumValMap map[string]int32) error {
- cval, err := convertEnum(value, f.Type(), enumValMap)
- if err != nil {
- return err
- }
- f.Set(cval)
- return nil
- }
- func populateFieldEnumRepeated(f reflect.Value, values []string, enumValMap map[string]int32) error {
- elemType := f.Type().Elem()
- f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)).Convert(f.Type()))
- for i, v := range values {
- result, err := convertEnum(v, elemType, enumValMap)
- if err != nil {
- return err
- }
- f.Index(i).Set(result)
- }
- return nil
- }
- var (
- convFromType = map[reflect.Kind]reflect.Value{
- reflect.String: reflect.ValueOf(String),
- reflect.Bool: reflect.ValueOf(Bool),
- reflect.Float64: reflect.ValueOf(Float64),
- reflect.Float32: reflect.ValueOf(Float32),
- reflect.Int64: reflect.ValueOf(Int64),
- reflect.Int32: reflect.ValueOf(Int32),
- reflect.Uint64: reflect.ValueOf(Uint64),
- reflect.Uint32: reflect.ValueOf(Uint32),
- reflect.Slice: reflect.ValueOf(Bytes),
- }
- )
|