master
parent
75c9c1c909
commit
434d858718
42 changed files with 1523 additions and 68 deletions
@ -0,0 +1,224 @@ |
||||
package form |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"fmt" |
||||
"net/url" |
||||
"reflect" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"google.golang.org/protobuf/proto" |
||||
"google.golang.org/protobuf/reflect/protoreflect" |
||||
"google.golang.org/protobuf/types/known/fieldmaskpb" |
||||
) |
||||
|
||||
// EncodeValues encode a message into url values.
|
||||
func EncodeValues(msg interface{}) (url.Values, error) { |
||||
if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) { |
||||
return url.Values{}, nil |
||||
} |
||||
if v, ok := msg.(proto.Message); ok { |
||||
u := make(url.Values) |
||||
err := encodeByField(u, "", v.ProtoReflect()) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return u, nil |
||||
} |
||||
return encoder.Encode(msg) |
||||
} |
||||
|
||||
func encodeByField(u url.Values, path string, m protoreflect.Message) (finalErr error) { |
||||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
||||
var ( |
||||
key string |
||||
newPath string |
||||
) |
||||
if fd.HasJSONName() { |
||||
key = fd.JSONName() |
||||
} else { |
||||
key = fd.TextName() |
||||
} |
||||
if path == "" { |
||||
newPath = key |
||||
} else { |
||||
newPath = path + "." + key |
||||
} |
||||
if of := fd.ContainingOneof(); of != nil { |
||||
if f := m.WhichOneof(of); f != nil && f != fd { |
||||
return true |
||||
} |
||||
} |
||||
switch { |
||||
case fd.IsList(): |
||||
if v.List().Len() > 0 { |
||||
list, err := encodeRepeatedField(fd, v.List()) |
||||
if err != nil { |
||||
finalErr = err |
||||
return false |
||||
} |
||||
for _, item := range list { |
||||
u.Add(newPath, item) |
||||
} |
||||
} |
||||
case fd.IsMap(): |
||||
if v.Map().Len() > 0 { |
||||
m, err := encodeMapField(fd, v.Map()) |
||||
if err != nil { |
||||
finalErr = err |
||||
return false |
||||
} |
||||
for k, value := range m { |
||||
u.Set(fmt.Sprintf("%s[%s]", newPath, k), value) |
||||
} |
||||
} |
||||
case (fd.Kind() == protoreflect.MessageKind) || (fd.Kind() == protoreflect.GroupKind): |
||||
value, err := encodeMessage(fd.Message(), v) |
||||
if err == nil { |
||||
u.Set(newPath, value) |
||||
return true |
||||
} |
||||
if err = encodeByField(u, newPath, v.Message()); err != nil { |
||||
finalErr = err |
||||
return false |
||||
} |
||||
default: |
||||
value, err := EncodeField(fd, v) |
||||
if err != nil { |
||||
finalErr = err |
||||
return false |
||||
} |
||||
u.Set(newPath, value) |
||||
} |
||||
return true |
||||
}) |
||||
return |
||||
} |
||||
|
||||
func encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List) ([]string, error) { |
||||
var values []string |
||||
for i := 0; i < list.Len(); i++ { |
||||
value, err := EncodeField(fieldDescriptor, list.Get(i)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
values = append(values, value) |
||||
} |
||||
return values, nil |
||||
} |
||||
|
||||
func encodeMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map) (map[string]string, error) { |
||||
m := make(map[string]string) |
||||
mp.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { |
||||
key, err := EncodeField(fieldDescriptor.MapValue(), k.Value()) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
value, err := EncodeField(fieldDescriptor.MapValue(), v) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
m[key] = value |
||||
return true |
||||
}) |
||||
|
||||
return m, nil |
||||
} |
||||
|
||||
// EncodeField encode proto message filed
|
||||
func EncodeField(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value) (string, error) { |
||||
switch fieldDescriptor.Kind() { |
||||
case protoreflect.BoolKind: |
||||
return strconv.FormatBool(value.Bool()), nil |
||||
case protoreflect.EnumKind: |
||||
if fieldDescriptor.Enum().FullName() == "google.protobuf.NullValue" { |
||||
return nullStr, nil |
||||
} |
||||
desc := fieldDescriptor.Enum().Values().ByNumber(value.Enum()) |
||||
return string(desc.Name()), nil |
||||
case protoreflect.StringKind: |
||||
return value.String(), nil |
||||
case protoreflect.BytesKind: |
||||
return base64.URLEncoding.EncodeToString(value.Bytes()), nil |
||||
case protoreflect.MessageKind, protoreflect.GroupKind: |
||||
return encodeMessage(fieldDescriptor.Message(), value) |
||||
default: |
||||
return fmt.Sprint(value.Interface()), nil |
||||
} |
||||
} |
||||
|
||||
// encodeMessage marshals the fields in the given protoreflect.Message.
|
||||
// If the typeURL is non-empty, then a synthetic "@type" field is injected
|
||||
// containing the URL as the value.
|
||||
func encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protoreflect.Value) (string, error) { |
||||
switch msgDescriptor.FullName() { |
||||
case timestampMessageFullname: |
||||
return marshalTimestamp(value.Message()) |
||||
case durationMessageFullname: |
||||
return marshalDuration(value.Message()) |
||||
case bytesMessageFullname: |
||||
return marshalBytes(value.Message()) |
||||
case "google.protobuf.DoubleValue", "google.protobuf.FloatValue", "google.protobuf.Int64Value", "google.protobuf.Int32Value", |
||||
"google.protobuf.UInt64Value", "google.protobuf.UInt32Value", "google.protobuf.BoolValue", "google.protobuf.StringValue": |
||||
fd := msgDescriptor.Fields() |
||||
v := value.Message().Get(fd.ByName("value")) |
||||
return fmt.Sprint(v.Interface()), nil |
||||
case fieldMaskFullName: |
||||
m, ok := value.Message().Interface().(*fieldmaskpb.FieldMask) |
||||
if !ok || m == nil { |
||||
return "", nil |
||||
} |
||||
for i, v := range m.Paths { |
||||
m.Paths[i] = jsonCamelCase(v) |
||||
} |
||||
return strings.Join(m.Paths, ","), nil |
||||
default: |
||||
return "", fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName())) |
||||
} |
||||
} |
||||
|
||||
// EncodeFieldMask return field mask name=paths
|
||||
func EncodeFieldMask(m protoreflect.Message) (query string) { |
||||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
||||
if fd.Kind() == protoreflect.MessageKind { |
||||
if msg := fd.Message(); msg.FullName() == fieldMaskFullName { |
||||
value, err := encodeMessage(msg, v) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
if fd.HasJSONName() { |
||||
query = fd.JSONName() + "=" + value |
||||
} else { |
||||
query = fd.TextName() + "=" + value |
||||
} |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
return |
||||
} |
||||
|
||||
// jsonCamelCase converts a snake_case identifier to a camelCase identifier,
|
||||
// according to the protobuf JSON specification.
|
||||
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L842
|
||||
func jsonCamelCase(s string) string { |
||||
var b []byte |
||||
var wasUnderscore bool |
||||
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
|
||||
c := s[i] |
||||
if c != '_' { |
||||
if wasUnderscore && isASCIILower(c) { |
||||
c -= 'a' - 'A' // convert to uppercase
|
||||
} |
||||
b = append(b, c) |
||||
} |
||||
wasUnderscore = c == '_' |
||||
} |
||||
return string(b) |
||||
} |
||||
|
||||
func isASCIILower(c byte) bool { |
||||
return 'a' <= c && c <= 'z' |
||||
} |
@ -0,0 +1,333 @@ |
||||
package form |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"errors" |
||||
"fmt" |
||||
"net/url" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
|
||||
"google.golang.org/protobuf/encoding/protojson" |
||||
"google.golang.org/protobuf/proto" |
||||
"google.golang.org/protobuf/reflect/protoreflect" |
||||
"google.golang.org/protobuf/reflect/protoregistry" |
||||
"google.golang.org/protobuf/types/known/durationpb" |
||||
"google.golang.org/protobuf/types/known/fieldmaskpb" |
||||
"google.golang.org/protobuf/types/known/structpb" |
||||
"google.golang.org/protobuf/types/known/timestamppb" |
||||
"google.golang.org/protobuf/types/known/wrapperspb" |
||||
) |
||||
|
||||
// DecodeValues decode url value into proto message.
|
||||
func DecodeValues(msg proto.Message, values url.Values) error { |
||||
for key, values := range values { |
||||
if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), values); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func populateFieldValues(v protoreflect.Message, fieldPath []string, values []string) error { |
||||
if len(fieldPath) < 1 { |
||||
return errors.New("no field path") |
||||
} |
||||
if len(values) < 1 { |
||||
return errors.New("no value provided") |
||||
} |
||||
|
||||
var fd protoreflect.FieldDescriptor |
||||
for i, fieldName := range fieldPath { |
||||
if fd = getFieldDescriptor(v, fieldName); fd == nil { |
||||
// ignore unexpected field.
|
||||
return nil |
||||
} |
||||
|
||||
if i == len(fieldPath)-1 { |
||||
break |
||||
} |
||||
|
||||
if fd.Message() == nil || fd.Cardinality() == protoreflect.Repeated { |
||||
if fd.IsMap() && len(fieldPath) > 1 { |
||||
// post subfield
|
||||
return populateMapField(fd, v.Mutable(fd).Map(), []string{fieldPath[1]}, values) |
||||
} |
||||
return fmt.Errorf("invalid path: %q is not a message", fieldName) |
||||
} |
||||
|
||||
v = v.Mutable(fd).Message() |
||||
} |
||||
if of := fd.ContainingOneof(); of != nil { |
||||
if f := v.WhichOneof(of); f != nil { |
||||
return fmt.Errorf("field already set for oneof %q", of.FullName().Name()) |
||||
} |
||||
} |
||||
switch { |
||||
case fd.IsList(): |
||||
return populateRepeatedField(fd, v.Mutable(fd).List(), values) |
||||
case fd.IsMap(): |
||||
return populateMapField(fd, v.Mutable(fd).Map(), fieldPath, values) |
||||
} |
||||
if len(values) > 1 { |
||||
return fmt.Errorf("too many values for field %q: %s", fd.FullName().Name(), strings.Join(values, ", ")) |
||||
} |
||||
return populateField(fd, v, values[0]) |
||||
} |
||||
|
||||
func getFieldDescriptor(v protoreflect.Message, fieldName string) protoreflect.FieldDescriptor { |
||||
fields := v.Descriptor().Fields() |
||||
var fd protoreflect.FieldDescriptor |
||||
if fd = getDescriptorByFieldAndName(fields, fieldName); fd == nil { |
||||
if v.Descriptor().FullName() == structMessageFullname { |
||||
fd = fields.ByNumber(structFieldsFieldNumber) |
||||
} else if len(fieldName) > 2 && strings.HasSuffix(fieldName, "[]") { |
||||
fd = getDescriptorByFieldAndName(fields, strings.TrimSuffix(fieldName, "[]")) |
||||
} |
||||
} |
||||
return fd |
||||
} |
||||
|
||||
func getDescriptorByFieldAndName(fields protoreflect.FieldDescriptors, fieldName string) protoreflect.FieldDescriptor { |
||||
var fd protoreflect.FieldDescriptor |
||||
if fd = fields.ByName(protoreflect.Name(fieldName)); fd == nil { |
||||
fd = fields.ByJSONName(fieldName) |
||||
} |
||||
return fd |
||||
} |
||||
|
||||
func populateField(fd protoreflect.FieldDescriptor, v protoreflect.Message, value string) error { |
||||
if value == "" { |
||||
return nil |
||||
} |
||||
val, err := parseField(fd, value) |
||||
if err != nil { |
||||
return fmt.Errorf("parsing field %q: %w", fd.FullName().Name(), err) |
||||
} |
||||
v.Set(fd, val) |
||||
return nil |
||||
} |
||||
|
||||
func populateRepeatedField(fd protoreflect.FieldDescriptor, list protoreflect.List, values []string) error { |
||||
for _, value := range values { |
||||
v, err := parseField(fd, value) |
||||
if err != nil { |
||||
return fmt.Errorf("parsing list %q: %w", fd.FullName().Name(), err) |
||||
} |
||||
list.Append(v) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func populateMapField(fd protoreflect.FieldDescriptor, mp protoreflect.Map, fieldPath []string, values []string) error { |
||||
// post sub key.
|
||||
nkey := len(fieldPath) - 1 |
||||
key, err := parseField(fd.MapKey(), fieldPath[nkey]) |
||||
if err != nil { |
||||
return fmt.Errorf("parsing map key %q: %w", fd.FullName().Name(), err) |
||||
} |
||||
vkey := len(values) - 1 |
||||
value, err := parseField(fd.MapValue(), values[vkey]) |
||||
if err != nil { |
||||
return fmt.Errorf("parsing map value %q: %w", fd.FullName().Name(), err) |
||||
} |
||||
mp.Set(key.MapKey(), value) |
||||
return nil |
||||
} |
||||
|
||||
func parseField(fd protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) { |
||||
switch fd.Kind() { |
||||
case protoreflect.BoolKind: |
||||
v, err := strconv.ParseBool(value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfBool(v), nil |
||||
case protoreflect.EnumKind: |
||||
enum, err := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()) |
||||
switch { |
||||
case errors.Is(err, protoregistry.NotFound): |
||||
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fd.Enum().FullName()) |
||||
case err != nil: |
||||
return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err) |
||||
} |
||||
v := enum.Descriptor().Values().ByName(protoreflect.Name(value)) |
||||
if v == nil { |
||||
i, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) |
||||
} |
||||
v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i)) |
||||
if v == nil { |
||||
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) |
||||
} |
||||
} |
||||
return protoreflect.ValueOfEnum(v.Number()), nil |
||||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: |
||||
v, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfInt32(int32(v)), nil |
||||
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
||||
v, err := strconv.ParseInt(value, 10, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfInt64(v), nil |
||||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: |
||||
v, err := strconv.ParseUint(value, 10, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfUint32(uint32(v)), nil |
||||
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
||||
v, err := strconv.ParseUint(value, 10, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfUint64(v), nil |
||||
case protoreflect.FloatKind: |
||||
v, err := strconv.ParseFloat(value, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfFloat32(float32(v)), nil |
||||
case protoreflect.DoubleKind: |
||||
v, err := strconv.ParseFloat(value, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfFloat64(v), nil |
||||
case protoreflect.StringKind: |
||||
return protoreflect.ValueOfString(value), nil |
||||
case protoreflect.BytesKind: |
||||
v, err := base64.StdEncoding.DecodeString(value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
return protoreflect.ValueOfBytes(v), nil |
||||
case protoreflect.MessageKind, protoreflect.GroupKind: |
||||
return parseMessage(fd.Message(), value) |
||||
default: |
||||
panic(fmt.Sprintf("unknown field kind: %v", fd.Kind())) |
||||
} |
||||
} |
||||
|
||||
func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect.Value, error) { |
||||
var msg proto.Message |
||||
switch md.FullName() { |
||||
case "google.protobuf.Timestamp": |
||||
if value == nullStr { |
||||
break |
||||
} |
||||
t, err := time.Parse(time.RFC3339Nano, value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = timestamppb.New(t) |
||||
case "google.protobuf.Duration": |
||||
if value == nullStr { |
||||
break |
||||
} |
||||
d, err := time.ParseDuration(value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = durationpb.New(d) |
||||
case "google.protobuf.DoubleValue": |
||||
v, err := strconv.ParseFloat(value, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.Double(v) |
||||
case "google.protobuf.FloatValue": |
||||
v, err := strconv.ParseFloat(value, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.Float(float32(v)) |
||||
case "google.protobuf.Int64Value": |
||||
v, err := strconv.ParseInt(value, 10, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.Int64(v) |
||||
case "google.protobuf.Int32Value": |
||||
v, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.Int32(int32(v)) |
||||
case "google.protobuf.UInt64Value": |
||||
v, err := strconv.ParseUint(value, 10, 64) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.UInt64(v) |
||||
case "google.protobuf.UInt32Value": |
||||
v, err := strconv.ParseUint(value, 10, 32) //nolint:gomnd
|
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.UInt32(uint32(v)) |
||||
case "google.protobuf.BoolValue": |
||||
v, err := strconv.ParseBool(value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = wrapperspb.Bool(v) |
||||
case "google.protobuf.StringValue": |
||||
msg = wrapperspb.String(value) |
||||
case "google.protobuf.BytesValue": |
||||
v, err := base64.StdEncoding.DecodeString(value) |
||||
if err != nil { |
||||
if v, err = base64.URLEncoding.DecodeString(value); err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
} |
||||
msg = wrapperspb.Bytes(v) |
||||
case "google.protobuf.FieldMask": |
||||
fm := &fieldmaskpb.FieldMask{} |
||||
for _, fv := range strings.Split(value, ",") { |
||||
fm.Paths = append(fm.Paths, jsonSnakeCase(fv)) |
||||
} |
||||
msg = fm |
||||
case "google.protobuf.Value": |
||||
fm, err := structpb.NewValue(value) |
||||
if err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = fm |
||||
case "google.protobuf.Struct": |
||||
var v structpb.Struct |
||||
if err := protojson.Unmarshal([]byte(value), &v); err != nil { |
||||
return protoreflect.Value{}, err |
||||
} |
||||
msg = &v |
||||
default: |
||||
return protoreflect.Value{}, fmt.Errorf("unsupported message type: %q", string(md.FullName())) |
||||
} |
||||
return protoreflect.ValueOfMessage(msg.ProtoReflect()), nil |
||||
} |
||||
|
||||
// jsonSnakeCase converts a camelCase identifier to a snake_case identifier,
|
||||
// according to the protobuf JSON specification.
|
||||
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L864
|
||||
func jsonSnakeCase(s string) string { |
||||
var b []byte |
||||
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
|
||||
c := s[i] |
||||
if isASCIIUpper(c) { |
||||
b = append(b, '_') |
||||
c += 'a' - 'A' // convert to lowercase
|
||||
} |
||||
b = append(b, c) |
||||
} |
||||
return string(b) |
||||
} |
||||
|
||||
func isASCIIUpper(c byte) bool { |
||||
return 'A' <= c && c <= 'Z' |
||||
} |
@ -0,0 +1,94 @@ |
||||
package form |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"fmt" |
||||
"math" |
||||
"strings" |
||||
"time" |
||||
|
||||
"google.golang.org/protobuf/reflect/protoreflect" |
||||
) |
||||
|
||||
const ( |
||||
// timestamp
|
||||
timestampMessageFullname protoreflect.FullName = "google.protobuf.Timestamp" |
||||
maxTimestampSeconds = 253402300799 |
||||
minTimestampSeconds = -6213559680013 |
||||
timestampSecondsFieldNumber protoreflect.FieldNumber = 1 |
||||
timestampNanosFieldNumber protoreflect.FieldNumber = 2 |
||||
|
||||
// duration
|
||||
durationMessageFullname protoreflect.FullName = "google.protobuf.Duration" |
||||
secondsInNanos = 999999999 |
||||
durationSecondsFieldNumber protoreflect.FieldNumber = 1 |
||||
durationNanosFieldNumber protoreflect.FieldNumber = 2 |
||||
|
||||
// bytes
|
||||
bytesMessageFullname protoreflect.FullName = "google.protobuf.BytesValue" |
||||
bytesValueFieldNumber protoreflect.FieldNumber = 1 |
||||
|
||||
// google.protobuf.Struct.
|
||||
structMessageFullname protoreflect.FullName = "google.protobuf.Struct" |
||||
structFieldsFieldNumber protoreflect.FieldNumber = 1 |
||||
|
||||
fieldMaskFullName protoreflect.FullName = "google.protobuf.FieldMask" |
||||
) |
||||
|
||||
func marshalTimestamp(m protoreflect.Message) (string, error) { |
||||
fds := m.Descriptor().Fields() |
||||
fdSeconds := fds.ByNumber(timestampSecondsFieldNumber) |
||||
fdNanos := fds.ByNumber(timestampNanosFieldNumber) |
||||
|
||||
secsVal := m.Get(fdSeconds) |
||||
nanosVal := m.Get(fdNanos) |
||||
secs := secsVal.Int() |
||||
nanos := nanosVal.Int() |
||||
if secs < minTimestampSeconds || secs > maxTimestampSeconds { |
||||
return "", fmt.Errorf("%s: seconds out of range %v", timestampMessageFullname, secs) |
||||
} |
||||
if nanos < 0 || nanos > secondsInNanos { |
||||
return "", fmt.Errorf("%s: nanos out of range %v", timestampMessageFullname, nanos) |
||||
} |
||||
// Uses RFC 3339, where generated output will be Z-normalized and uses 0, 3,
|
||||
// 6 or 9 fractional digits.
|
||||
t := time.Unix(secs, nanos).UTC() |
||||
x := t.Format("2006-01-02T15:04:05.000000000") |
||||
x = strings.TrimSuffix(x, "000") |
||||
x = strings.TrimSuffix(x, "000") |
||||
x = strings.TrimSuffix(x, ".000") |
||||
return x + "Z", nil |
||||
} |
||||
|
||||
func marshalDuration(m protoreflect.Message) (string, error) { |
||||
fds := m.Descriptor().Fields() |
||||
fdSeconds := fds.ByNumber(durationSecondsFieldNumber) |
||||
fdNanos := fds.ByNumber(durationNanosFieldNumber) |
||||
|
||||
secsVal := m.Get(fdSeconds) |
||||
nanosVal := m.Get(fdNanos) |
||||
secs := secsVal.Int() |
||||
nanos := nanosVal.Int() |
||||
d := time.Duration(secs) * time.Second |
||||
overflow := d/time.Second != time.Duration(secs) |
||||
d += time.Duration(nanos) * time.Nanosecond |
||||
overflow = overflow || (secs < 0 && nanos < 0 && d > 0) |
||||
overflow = overflow || (secs > 0 && nanos > 0 && d < 0) |
||||
if overflow { |
||||
switch { |
||||
case secs < 0: |
||||
return time.Duration(math.MinInt64).String(), nil |
||||
case secs > 0: |
||||
return time.Duration(math.MaxInt64).String(), nil |
||||
} |
||||
} |
||||
return d.String(), nil |
||||
} |
||||
|
||||
func marshalBytes(m protoreflect.Message) (string, error) { |
||||
fds := m.Descriptor().Fields() |
||||
fdBytes := fds.ByNumber(bytesValueFieldNumber) |
||||
bytesVal := m.Get(fdBytes) |
||||
val := bytesVal.Bytes() |
||||
return base64.StdEncoding.EncodeToString(val), nil |
||||
} |
@ -0,0 +1,67 @@ |
||||
package json |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"git.diulo.com/mogfee/kit/encoding" |
||||
"reflect" |
||||
|
||||
"google.golang.org/protobuf/encoding/protojson" |
||||
"google.golang.org/protobuf/proto" |
||||
) |
||||
|
||||
// Name is the name registered for the json codec.
|
||||
const Name = "json" |
||||
|
||||
var ( |
||||
// MarshalOptions is a configurable JSON format marshaller.
|
||||
MarshalOptions = protojson.MarshalOptions{ |
||||
EmitUnpopulated: true, |
||||
} |
||||
// UnmarshalOptions is a configurable JSON format parser.
|
||||
UnmarshalOptions = protojson.UnmarshalOptions{ |
||||
DiscardUnknown: true, |
||||
} |
||||
) |
||||
|
||||
func init() { |
||||
encoding.RegisterCodec(codec{}) |
||||
} |
||||
|
||||
// codec is a Codec implementation with json.
|
||||
type codec struct{} |
||||
|
||||
func (codec) Marshal(v interface{}) ([]byte, error) { |
||||
switch m := v.(type) { |
||||
case json.Marshaler: |
||||
return m.MarshalJSON() |
||||
case proto.Message: |
||||
return MarshalOptions.Marshal(m) |
||||
default: |
||||
return json.Marshal(m) |
||||
} |
||||
} |
||||
|
||||
func (codec) Unmarshal(data []byte, v interface{}) error { |
||||
switch m := v.(type) { |
||||
case json.Unmarshaler: |
||||
return m.UnmarshalJSON(data) |
||||
case proto.Message: |
||||
return UnmarshalOptions.Unmarshal(data, m) |
||||
default: |
||||
rv := reflect.ValueOf(v) |
||||
for rv := rv; rv.Kind() == reflect.Ptr; { |
||||
if rv.IsNil() { |
||||
rv.Set(reflect.New(rv.Type().Elem())) |
||||
} |
||||
rv = rv.Elem() |
||||
} |
||||
if m, ok := reflect.Indirect(rv).Interface().(proto.Message); ok { |
||||
return UnmarshalOptions.Unmarshal(data, m) |
||||
} |
||||
return json.Unmarshal(data, m) |
||||
} |
||||
} |
||||
|
||||
func (codec) Name() string { |
||||
return Name |
||||
} |
@ -0,0 +1,226 @@ |
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v4.22.0
|
||||
// source: errors.proto
|
||||
|
||||
package errors |
||||
|
||||
import ( |
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect" |
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl" |
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb" |
||||
reflect "reflect" |
||||
sync "sync" |
||||
) |
||||
|
||||
const ( |
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) |
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) |
||||
) |
||||
|
||||
type Status struct { |
||||
state protoimpl.MessageState |
||||
sizeCache protoimpl.SizeCache |
||||
unknownFields protoimpl.UnknownFields |
||||
|
||||
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` |
||||
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` |
||||
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` |
||||
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` |
||||
} |
||||
|
||||
func (x *Status) Reset() { |
||||
*x = Status{} |
||||
if protoimpl.UnsafeEnabled { |
||||
mi := &file_errors_proto_msgTypes[0] |
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||
ms.StoreMessageInfo(mi) |
||||
} |
||||
} |
||||
|
||||
func (x *Status) String() string { |
||||
return protoimpl.X.MessageStringOf(x) |
||||
} |
||||
|
||||
func (*Status) ProtoMessage() {} |
||||
|
||||
func (x *Status) ProtoReflect() protoreflect.Message { |
||||
mi := &file_errors_proto_msgTypes[0] |
||||
if protoimpl.UnsafeEnabled && x != nil { |
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||
if ms.LoadMessageInfo() == nil { |
||||
ms.StoreMessageInfo(mi) |
||||
} |
||||
return ms |
||||
} |
||||
return mi.MessageOf(x) |
||||
} |
||||
|
||||
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
|
||||
func (*Status) Descriptor() ([]byte, []int) { |
||||
return file_errors_proto_rawDescGZIP(), []int{0} |
||||
} |
||||
|
||||
func (x *Status) GetCode() int32 { |
||||
if x != nil { |
||||
return x.Code |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
func (x *Status) GetReason() string { |
||||
if x != nil { |
||||
return x.Reason |
||||
} |
||||
return "" |
||||
} |
||||
|
||||
func (x *Status) GetMessage() string { |
||||
if x != nil { |
||||
return x.Message |
||||
} |
||||
return "" |
||||
} |
||||
|
||||
func (x *Status) GetMetadata() map[string]string { |
||||
if x != nil { |
||||
return x.Metadata |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
var file_errors_proto_extTypes = []protoimpl.ExtensionInfo{ |
||||
{ |
||||
ExtendedType: (*descriptorpb.EnumOptions)(nil), |
||||
ExtensionType: (*int32)(nil), |
||||
Field: 1108, |
||||
Name: "errors.default_code", |
||||
Tag: "varint,1108,opt,name=default_code", |
||||
Filename: "errors.proto", |
||||
}, |
||||
{ |
||||
ExtendedType: (*descriptorpb.EnumValueOptions)(nil), |
||||
ExtensionType: (*int32)(nil), |
||||
Field: 1109, |
||||
Name: "errors.code", |
||||
Tag: "varint,1109,opt,name=code", |
||||
Filename: "errors.proto", |
||||
}, |
||||
} |
||||
|
||||
// Extension fields to descriptorpb.EnumOptions.
|
||||
var ( |
||||
// optional int32 default_code = 1108;
|
||||
E_DefaultCode = &file_errors_proto_extTypes[0] |
||||
) |
||||
|
||||
// Extension fields to descriptorpb.EnumValueOptions.
|
||||
var ( |
||||
// optional int32 code = 1109;
|
||||
E_Code = &file_errors_proto_extTypes[1] |
||||
) |
||||
|
||||
var File_errors_proto protoreflect.FileDescriptor |
||||
|
||||
var file_errors_proto_rawDesc = []byte{ |
||||
0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, |
||||
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, |
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, |
||||
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, |
||||
0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, |
||||
0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, |
||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, |
||||
0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, |
||||
0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, |
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x72, |
||||
0x72, 0x6f, 0x72, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, |
||||
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, |
||||
0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, |
||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, |
||||
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, |
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, |
||||
0x3a, 0x40, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, |
||||
0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, |
||||
0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, |
||||
0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, |
||||
0x64, 0x65, 0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, |
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, |
||||
0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, |
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, |
||||
0x74, 0x2e, 0x64, 0x69, 0x75, 0x6c, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x67, 0x66, |
||||
0x65, 0x65, 0x2f, 0x6b, 0x69, 0x74, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, |
||||
0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, |
||||
} |
||||
|
||||
var ( |
||||
file_errors_proto_rawDescOnce sync.Once |
||||
file_errors_proto_rawDescData = file_errors_proto_rawDesc |
||||
) |
||||
|
||||
func file_errors_proto_rawDescGZIP() []byte { |
||||
file_errors_proto_rawDescOnce.Do(func() { |
||||
file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData) |
||||
}) |
||||
return file_errors_proto_rawDescData |
||||
} |
||||
|
||||
var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) |
||||
var file_errors_proto_goTypes = []interface{}{ |
||||
(*Status)(nil), // 0: errors.Status
|
||||
nil, // 1: errors.Status.MetadataEntry
|
||||
(*descriptorpb.EnumOptions)(nil), // 2: google.protobuf.EnumOptions
|
||||
(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions
|
||||
} |
||||
var file_errors_proto_depIdxs = []int32{ |
||||
1, // 0: errors.Status.metadata:type_name -> errors.Status.MetadataEntry
|
||||
2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions
|
||||
3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
1, // [1:3] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
} |
||||
|
||||
func init() { file_errors_proto_init() } |
||||
func file_errors_proto_init() { |
||||
if File_errors_proto != nil { |
||||
return |
||||
} |
||||
if !protoimpl.UnsafeEnabled { |
||||
file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { |
||||
switch v := v.(*Status); i { |
||||
case 0: |
||||
return &v.state |
||||
case 1: |
||||
return &v.sizeCache |
||||
case 2: |
||||
return &v.unknownFields |
||||
default: |
||||
return nil |
||||
} |
||||
} |
||||
} |
||||
type x struct{} |
||||
out := protoimpl.TypeBuilder{ |
||||
File: protoimpl.DescBuilder{ |
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), |
||||
RawDescriptor: file_errors_proto_rawDesc, |
||||
NumEnums: 0, |
||||
NumMessages: 2, |
||||
NumExtensions: 2, |
||||
NumServices: 0, |
||||
}, |
||||
GoTypes: file_errors_proto_goTypes, |
||||
DependencyIndexes: file_errors_proto_depIdxs, |
||||
MessageInfos: file_errors_proto_msgTypes, |
||||
ExtensionInfos: file_errors_proto_extTypes, |
||||
}.Build() |
||||
File_errors_proto = out.File |
||||
file_errors_proto_rawDesc = nil |
||||
file_errors_proto_goTypes = nil |
||||
file_errors_proto_depIdxs = nil |
||||
} |
@ -0,0 +1,16 @@ |
||||
syntax="proto3"; |
||||
package errors; |
||||
option go_package="git.diulo.com/mogfee/kit/errors;errors"; |
||||
import "google/protobuf/descriptor.proto"; |
||||
message Status{ |
||||
int32 code=1; |
||||
string reason=2; |
||||
string message=3; |
||||
map<string,string>metadata=4; |
||||
} |
||||
extend google.protobuf.EnumOptions{ |
||||
int32 default_code=1108; |
||||
} |
||||
extend google.protobuf.EnumValueOptions{ |
||||
int32 code=1109; |
||||
} |
@ -0,0 +1,16 @@ |
||||
package errors |
||||
|
||||
import ( |
||||
stderrors "errors" |
||||
) |
||||
|
||||
func Is(err, target error) bool { |
||||
return stderrors.Is(err, target) |
||||
} |
||||
|
||||
func As(err error, target any) bool { |
||||
return stderrors.As(err, target) |
||||
} |
||||
func Unwarp(err error) error { |
||||
return stderrors.Unwrap(err) |
||||
} |
@ -0,0 +1,34 @@ |
||||
package endpoint |
||||
|
||||
import ( |
||||
"net/url" |
||||
) |
||||
|
||||
// NewEndpoint new an Endpoint URL.
|
||||
func NewEndpoint(scheme, host string) *url.URL { |
||||
return &url.URL{Scheme: scheme, Host: host} |
||||
} |
||||
|
||||
// ParseEndpoint parses an Endpoint URL.
|
||||
func ParseEndpoint(endpoints []string, scheme string) (string, error) { |
||||
for _, e := range endpoints { |
||||
u, err := url.Parse(e) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
if u.Scheme == scheme { |
||||
return u.Host, nil |
||||
} |
||||
} |
||||
return "", nil |
||||
} |
||||
|
||||
// Scheme is the scheme of endpoint url.
|
||||
// examples: scheme="http",isSecure=true get "https"
|
||||
func Scheme(scheme string, isSecure bool) string { |
||||
if isSecure { |
||||
return scheme + "s" |
||||
} |
||||
return scheme |
||||
} |
@ -0,0 +1,92 @@ |
||||
package host |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net" |
||||
"strconv" |
||||
) |
||||
|
||||
// ExtractHostPort from address
|
||||
func ExtractHostPort(addr string) (host string, port uint64, err error) { |
||||
var ports string |
||||
host, ports, err = net.SplitHostPort(addr) |
||||
if err != nil { |
||||
return |
||||
} |
||||
port, err = strconv.ParseUint(ports, 10, 16) //nolint:gomnd
|
||||
return |
||||
} |
||||
|
||||
func isValidIP(addr string) bool { |
||||
ip := net.ParseIP(addr) |
||||
return ip.IsGlobalUnicast() && !ip.IsInterfaceLocalMulticast() |
||||
} |
||||
|
||||
// Port return a real port.
|
||||
func Port(lis net.Listener) (int, bool) { |
||||
if addr, ok := lis.Addr().(*net.TCPAddr); ok { |
||||
return addr.Port, true |
||||
} |
||||
return 0, false |
||||
} |
||||
|
||||
// Extract returns a private addr and port.
|
||||
func Extract(hostPort string, lis net.Listener) (string, error) { |
||||
addr, port, err := net.SplitHostPort(hostPort) |
||||
if err != nil && lis == nil { |
||||
return "", err |
||||
} |
||||
if lis != nil { |
||||
p, ok := Port(lis) |
||||
if !ok { |
||||
return "", fmt.Errorf("failed to extract port: %v", lis.Addr()) |
||||
} |
||||
port = strconv.Itoa(p) |
||||
} |
||||
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") { |
||||
return net.JoinHostPort(addr, port), nil |
||||
} |
||||
ifaces, err := net.Interfaces() |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
minIndex := int(^uint(0) >> 1) |
||||
ips := make([]net.IP, 0) |
||||
for _, iface := range ifaces { |
||||
if (iface.Flags & net.FlagUp) == 0 { |
||||
continue |
||||
} |
||||
if iface.Index >= minIndex && len(ips) != 0 { |
||||
continue |
||||
} |
||||
addrs, err := iface.Addrs() |
||||
if err != nil { |
||||
continue |
||||
} |
||||
for i, rawAddr := range addrs { |
||||
var ip net.IP |
||||
switch addr := rawAddr.(type) { |
||||
case *net.IPAddr: |
||||
ip = addr.IP |
||||
case *net.IPNet: |
||||
ip = addr.IP |
||||
default: |
||||
continue |
||||
} |
||||
if isValidIP(ip.String()) { |
||||
minIndex = iface.Index |
||||
if i == 0 { |
||||
ips = make([]net.IP, 0, 1) |
||||
} |
||||
ips = append(ips, ip) |
||||
if ip.To4() != nil { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if len(ips) != 0 { |
||||
return net.JoinHostPort(ips[len(ips)-1].String(), port), nil |
||||
} |
||||
return "", nil |
||||
} |
@ -1,16 +1,36 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
protoc_gen_kit "git.diulo.com/mogfee/protoc-gen-kit" |
||||
"git.diulo.com/mogfee/kit" |
||||
"git.diulo.com/mogfee/kit/transport/http" |
||||
) |
||||
|
||||
func main() { |
||||
app := protoc_gen_kit.New( |
||||
protoc_gen_kit.Name("user-server"), |
||||
protoc_gen_kit.Server()) |
||||
app := kit.New( |
||||
kit.Name("user-server"), |
||||
kit.Server(appServer())) |
||||
fmt.Println("run start") |
||||
app.Run() |
||||
fmt.Println("run end") |
||||
app.Stop() |
||||
} |
||||
func appServer() *http.Server { |
||||
srv := http.NewServer(http.Address("localhost:9019")) |
||||
group := srv.Route("/") |
||||
group.Handle("GET", "/", func(ctx http.Context) error { |
||||
return errors.New("abc ") |
||||
}) |
||||
//group.GET("/", func(ctx http.Context) error {
|
||||
// return ctx.String(200, "index page")
|
||||
//})
|
||||
//group.GET("/hello", func(ctx http.Context) error {
|
||||
// return ctx.String(200, "hello page")
|
||||
//})
|
||||
//group.GET("/error", func(ctx http.Context) error {
|
||||
// fmt.Println("err 1")
|
||||
// return errors.New(400, "BAD_REQUEST", "")
|
||||
//})
|
||||
return srv |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
syntax="proto3"; |
||||
package errors; |
||||
option go_package="git.diulo.com/mogfee/kit/errors;errors"; |
||||
import "google/protobuf/descriptor.proto"; |
||||
message Status{ |
||||
int32 code=1; |
||||
string reason=2; |
||||
string message=3; |
||||
map<string,string>metadata=4; |
||||
} |
||||
extend google.protobuf.EnumOptions{ |
||||
int32 default_code=1108; |
||||
} |
||||
extend google.protobuf.EnumValueOptions{ |
||||
int32 code=1109; |
||||
} |
@ -0,0 +1,109 @@ |
||||
package http |
||||
|
||||
import ( |
||||
"net/http" |
||||
) |
||||
|
||||
// CallOption configures a Call before it starts or extracts information from
|
||||
// a Call after it completes.
|
||||
type CallOption interface { |
||||
// before is called before the call is sent to any server. If before
|
||||
// returns a non-nil error, the RPC fails with that error.
|
||||
before(*callInfo) error |
||||
|
||||
// after is called after the call has completed. after cannot return an
|
||||
// error, so any failures should be reported via output parameters.
|
||||
after(*callInfo, *csAttempt) |
||||
} |
||||
|
||||
type callInfo struct { |
||||
contentType string |
||||
operation string |
||||
pathTemplate string |
||||
} |
||||
|
||||
// EmptyCallOption does not alter the Call configuration.
|
||||
// It can be embedded in another structure to carry satellite data for use
|
||||
// by interceptors.
|
||||
type EmptyCallOption struct{} |
||||
|
||||
func (EmptyCallOption) before(*callInfo) error { return nil } |
||||
func (EmptyCallOption) after(*callInfo, *csAttempt) {} |
||||
|
||||
type csAttempt struct { |
||||
res *http.Response |
||||
} |
||||
|
||||
// ContentType with request content type.
|
||||
func ContentType(contentType string) CallOption { |
||||
return ContentTypeCallOption{ContentType: contentType} |
||||
} |
||||
|
||||
// ContentTypeCallOption is BodyCallOption
|
||||
type ContentTypeCallOption struct { |
||||
EmptyCallOption |
||||
ContentType string |
||||
} |
||||
|
||||
func (o ContentTypeCallOption) before(c *callInfo) error { |
||||
c.contentType = o.ContentType |
||||
return nil |
||||
} |
||||
|
||||
func defaultCallInfo(path string) callInfo { |
||||
return callInfo{ |
||||
contentType: "application/json", |
||||
operation: path, |
||||
pathTemplate: path, |
||||
} |
||||
} |
||||
|
||||
// Operation is serviceMethod call option
|
||||
func Operation(operation string) CallOption { |
||||
return OperationCallOption{Operation: operation} |
||||
} |
||||
|
||||
// OperationCallOption is set ServiceMethod for client call
|
||||
type OperationCallOption struct { |
||||
EmptyCallOption |
||||
Operation string |
||||
} |
||||
|
||||
func (o OperationCallOption) before(c *callInfo) error { |
||||
c.operation = o.Operation |
||||
return nil |
||||
} |
||||
|
||||
// PathTemplate is http path template
|
||||
func PathTemplate(pattern string) CallOption { |
||||
return PathTemplateCallOption{Pattern: pattern} |
||||
} |
||||
|
||||
// PathTemplateCallOption is set path template for client call
|
||||
type PathTemplateCallOption struct { |
||||
EmptyCallOption |
||||
Pattern string |
||||
} |
||||
|
||||
func (o PathTemplateCallOption) before(c *callInfo) error { |
||||
c.pathTemplate = o.Pattern |
||||
return nil |
||||
} |
||||
|
||||
// Header returns a CallOptions that retrieves the http response header
|
||||
// from server reply.
|
||||
func Header(header *http.Header) CallOption { |
||||
return HeaderCallOption{header: header} |
||||
} |
||||
|
||||
// HeaderCallOption is retrieve response header for client call
|
||||
type HeaderCallOption struct { |
||||
EmptyCallOption |
||||
header *http.Header |
||||
} |
||||
|
||||
func (o HeaderCallOption) after(c *callInfo, cs *csAttempt) { |
||||
if cs.res != nil && cs.res.Header != nil { |
||||
*o.header = cs.res.Header |
||||
} |
||||
} |
@ -0,0 +1,96 @@ |
||||
package status |
||||
|
||||
import ( |
||||
"google.golang.org/grpc/codes" |
||||
"net/http" |
||||
) |
||||
|
||||
const ( |
||||
ClientClosed = 499 |
||||
) |
||||
|
||||
type Converter interface { |
||||
ToGRPCCode(code int) codes.Code |
||||
FromGRPCCode(code codes.Code) int |
||||
} |
||||
|
||||
var DefaultConverter Converter = statusConverter{} |
||||
|
||||
type statusConverter struct { |
||||
} |
||||
|
||||
func (s statusConverter) ToGRPCCode(code int) codes.Code { |
||||
switch code { |
||||
case http.StatusOK: |
||||
return codes.OK |
||||
case http.StatusBadRequest: |
||||
return codes.InvalidArgument |
||||
case http.StatusUnauthorized: |
||||
return codes.Unauthenticated |
||||
case http.StatusForbidden: |
||||
return codes.PermissionDenied |
||||
case http.StatusNotFound: |
||||
return codes.NotFound |
||||
case http.StatusConflict: |
||||
return codes.Aborted |
||||
case http.StatusTooManyRequests: |
||||
return codes.ResourceExhausted |
||||
case http.StatusInternalServerError: |
||||
return codes.Internal |
||||
case http.StatusNotImplemented: |
||||
return codes.Unimplemented |
||||
case http.StatusServiceUnavailable: |
||||
return codes.Unavailable |
||||
case http.StatusGatewayTimeout: |
||||
return codes.DeadlineExceeded |
||||
case ClientClosed: |
||||
return codes.Canceled |
||||
} |
||||
return codes.Unknown |
||||
} |
||||
|
||||
func (s statusConverter) FromGRPCCode(code codes.Code) int { |
||||
switch code { |
||||
case codes.OK: |
||||
return http.StatusOK |
||||
case codes.Canceled: |
||||
return ClientClosed |
||||
case codes.Unknown: |
||||
return http.StatusInternalServerError |
||||
case codes.InvalidArgument: |
||||
return http.StatusBadRequest |
||||
case codes.DeadlineExceeded: |
||||
return http.StatusGatewayTimeout |
||||
case codes.NotFound: |
||||
return http.StatusNotFound |
||||
case codes.AlreadyExists: |
||||
return http.StatusConflict |
||||
case codes.PermissionDenied: |
||||
return http.StatusForbidden |
||||
case codes.Unauthenticated: |
||||
return http.StatusUnauthorized |
||||
case codes.ResourceExhausted: |
||||
return http.StatusTooManyRequests |
||||
case codes.FailedPrecondition: |
||||
return http.StatusBadRequest |
||||
case codes.Aborted: |
||||
return http.StatusConflict |
||||
case codes.OutOfRange: |
||||
return http.StatusBadRequest |
||||
case codes.Unimplemented: |
||||
return http.StatusNotImplemented |
||||
case codes.Internal: |
||||
return http.StatusInternalServerError |
||||
case codes.Unavailable: |
||||
return http.StatusServiceUnavailable |
||||
case codes.DataLoss: |
||||
return http.StatusInternalServerError |
||||
} |
||||
return http.StatusInternalServerError |
||||
} |
||||
func ToGRPCCode(code int) codes.Code { |
||||
return DefaultConverter.ToGRPCCode(code) |
||||
} |
||||
func FromGRPCCode(code codes.Code) int { |
||||
return DefaultConverter.FromGRPCCode(code) |
||||
} |
Loading…
Reference in new issue