mirror of
https://github.com/linka-cloud/grpc.git
synced 2024-12-22 17:00:45 +00:00
options: fix interceptors, add defaulter and recovery
This commit is contained in:
parent
dc78a2c688
commit
d246bb3214
16
README.md
16
README.md
@ -9,28 +9,30 @@ Principles:
|
||||
|
||||
Features:
|
||||
- [x] simple configuration with options
|
||||
- [x] embeded gorm database with options (branch db)
|
||||
- [x] embedded gorm database with options (branch db)
|
||||
- [x] simple TLS configuration
|
||||
- [ ] TLS auth
|
||||
- [ ] client connection pool
|
||||
- [ ] registry / resolver resolution
|
||||
- [ ] mdns
|
||||
- [ ] kubernetes
|
||||
- [ ] default interceptors implementation:
|
||||
- [ ] default
|
||||
- [ ] validation
|
||||
- [ ] default interceptors implementation:
|
||||
- [ ] context request id
|
||||
- [x] defaulter
|
||||
- [x] validation
|
||||
- [ ] health
|
||||
- [ ] context logger
|
||||
- [ ] sentry
|
||||
- [ ] rate-limiting
|
||||
- [ ] ban
|
||||
- [ ] auth claim in context
|
||||
- [ ] recovery
|
||||
- [x] recovery (server side only)
|
||||
- [x] tracing (open-tracing)
|
||||
- [x] metrics (prometheus)
|
||||
- [ ] retries
|
||||
- [ ] context DB / transaction
|
||||
- ...
|
||||
- [ ] api gateway with middleware:
|
||||
- [ ] grpc web / api gateway with middleware:
|
||||
- [ ] auth
|
||||
- [ ] cors
|
||||
- [ ] logging
|
||||
@ -43,3 +45,5 @@ Features:
|
||||
- https://github.com/grpc-ecosystem/grpc-opentracing
|
||||
- https://github.com/grpc-ecosystem/go-grpc-prometheus
|
||||
- https://github.com/grpc-ecosystem/grpc-gateway
|
||||
- https://github.com/jaredfolkins/badactor
|
||||
- github.com/improbable-eng/grpc-web
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"go.linka.cloud/grpc/client"
|
||||
"go.linka.cloud/grpc/interceptors/defaulter"
|
||||
metrics2 "go.linka.cloud/grpc/interceptors/metrics"
|
||||
validation2 "go.linka.cloud/grpc/interceptors/validation"
|
||||
"go.linka.cloud/grpc/logger"
|
||||
@ -69,6 +70,7 @@ func main() {
|
||||
var err error
|
||||
metrics := metrics2.NewInterceptors()
|
||||
validation := validation2.NewInterceptors(true)
|
||||
defaulter := defaulter.NewInterceptors()
|
||||
address := "0.0.0.0:9991"
|
||||
svc, err = service.New(
|
||||
service.WithContext(ctx),
|
||||
@ -93,7 +95,7 @@ func main() {
|
||||
service.WithGRPCWeb(true),
|
||||
service.WithGRPCWebPrefix("/grpc"),
|
||||
service.WithMiddlewares(httpLogger),
|
||||
service.WithInterceptors(validation, metrics),
|
||||
service.WithInterceptors(metrics, defaulter, validation),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -125,7 +127,7 @@ func main() {
|
||||
if err == nil {
|
||||
logrus.Fatal("expected validation error")
|
||||
}
|
||||
stream, err := g.SayHelloStream(context.Background(), &HelloStreamRequest{Name: "test", Count: 10})
|
||||
stream, err := g.SayHelloStream(context.Background(), &HelloStreamRequest{Name: "test"})
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
@ -21,4 +21,7 @@ func (x *HelloReply) Default() {
|
||||
}
|
||||
|
||||
func (x *HelloStreamRequest) Default() {
|
||||
if x.Count == 0 {
|
||||
x.Count = 10
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
sync "sync"
|
||||
|
||||
_ "github.com/envoyproxy/protoc-gen-validate/validate"
|
||||
_ "go.linka.cloud/protoc-gen-defaults/defaults"
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
@ -180,32 +181,34 @@ var file_example_example_proto_rawDesc = []byte{
|
||||
0x72, 0x6c, 0x64, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||
0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x0c, 0x48, 0x65,
|
||||
0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10,
|
||||
0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x22, 0x49, 0x0a, 0x12, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28,
|
||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01, 0x0a,
|
||||
0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48,
|
||||
0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,
|
||||
0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
|
||||
0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c,
|
||||
0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15,
|
||||
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01, 0x2a, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48,
|
||||
0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72,
|
||||
0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e,
|
||||
0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x64, 0x65, 0x66, 0x61,
|
||||
0x75, 0x6c, 0x74, 0x73, 0x2f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x59, 0x0a, 0x12, 0x48, 0x65,
|
||||
0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09,
|
||||
0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x24, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x0e,
|
||||
0xfa, 0x42, 0x06, 0x22, 0x04, 0x18, 0x0a, 0x28, 0x01, 0x9a, 0x49, 0x02, 0x20, 0x0a, 0x52, 0x05,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65,
|
||||
0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,
|
||||
0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
|
||||
0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01,
|
||||
0x2a, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72,
|
||||
0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64,
|
||||
0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64,
|
||||
0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42,
|
||||
0x22, 0x5a, 0x20, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
|
||||
0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -249,7 +249,16 @@ func (m *HelloStreamRequest) Validate(all bool) error {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
// no validation rules for Count
|
||||
if val := m.GetCount(); val < 1 || val > 10 {
|
||||
err := HelloStreamRequestValidationError{
|
||||
field: "Count",
|
||||
reason: "value must be inside range [1, 10]",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return HelloStreamRequestMultiError(errors)
|
||||
|
@ -6,6 +6,7 @@ option go_package = "go.linka.cloud/grpc/example;main";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "defaults/defaults.proto";
|
||||
|
||||
service Greeter {
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {
|
||||
@ -27,5 +28,5 @@ message HelloReply {
|
||||
|
||||
message HelloStreamRequest {
|
||||
string name = 1 [(validate.rules).string = {min_len: 2, max_len: 40}];
|
||||
int64 count = 2;
|
||||
int64 count = 2 [(validate.rules).int64 = {gte: 1, lte: 10}, (defaults.value).int64 = 10];
|
||||
}
|
||||
|
77
interceptors/defaulter/interceptors.go
Normal file
77
interceptors/defaulter/interceptors.go
Normal file
@ -0,0 +1,77 @@
|
||||
package defaulter
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"go.linka.cloud/grpc/interceptors"
|
||||
)
|
||||
|
||||
type interceptor struct{}
|
||||
|
||||
func NewInterceptors() interceptors.Interceptors {
|
||||
return &interceptor{}
|
||||
}
|
||||
|
||||
func defaults(v interface{}) {
|
||||
if d, ok := v.(interface{ Default() }); v != nil && ok {
|
||||
d.Default()
|
||||
}
|
||||
}
|
||||
|
||||
func (i interceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
defaults(req)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (i interceptor) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
defaults(req)
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
func (i interceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
wrapper := &recvWrapper{ServerStream: stream}
|
||||
return handler(srv, wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
func (i interceptor) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
desc.Handler = (&sendWrapper{handler: desc.Handler}).Handler()
|
||||
return streamer(ctx, desc, cc, method)
|
||||
}
|
||||
}
|
||||
|
||||
type recvWrapper struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (s *recvWrapper) RecvMsg(m interface{}) error {
|
||||
if err := s.ServerStream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
defaults(m)
|
||||
return nil
|
||||
}
|
||||
|
||||
type sendWrapper struct {
|
||||
grpc.ServerStream
|
||||
handler grpc.StreamHandler
|
||||
}
|
||||
|
||||
func (s *sendWrapper) Handler() grpc.StreamHandler {
|
||||
return func(srv interface{}, stream grpc.ServerStream) error {
|
||||
return s.handler(srv, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sendWrapper) SendMsg(m interface{}) error {
|
||||
defaults(m)
|
||||
return s.ServerStream.SendMsg(m)
|
||||
}
|
29
interceptors/recovery/interceptors.go
Normal file
29
interceptors/recovery/interceptors.go
Normal file
@ -0,0 +1,29 @@
|
||||
package recovery
|
||||
|
||||
import (
|
||||
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type interceptors struct {
|
||||
opts grpc_recovery.Option
|
||||
}
|
||||
|
||||
func (i *interceptors) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||
return grpc_recovery.UnaryServerInterceptor(i.opts)
|
||||
}
|
||||
|
||||
func (i *interceptors) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||
return grpc_recovery.StreamServerInterceptor(i.opts)
|
||||
}
|
||||
|
||||
func (i *interceptors) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (i *interceptors) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
|
||||
|
@ -133,11 +133,9 @@ func (s *recvWrapper) RecvMsg(m interface{}) error {
|
||||
if err := s.ServerStream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.i.validate(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,6 @@ type options struct {
|
||||
|
||||
serverOpts []grpc.ServerOption
|
||||
|
||||
|
||||
unaryServerInterceptors []grpc.UnaryServerInterceptor
|
||||
streamServerInterceptors []grpc.StreamServerInterceptor
|
||||
|
||||
|
@ -110,7 +110,7 @@ func newService(opts ...Option) (*service, error) {
|
||||
ui := grpcmiddleware.ChainUnaryServer(s.opts.unaryServerInterceptors...)
|
||||
s.inproc = s.inproc.WithServerUnaryInterceptor(ui)
|
||||
|
||||
si := grpcmiddleware.ChainStreamServer( /*TODO(adphi): add to options*/ )
|
||||
si := grpcmiddleware.ChainStreamServer(s.opts.streamServerInterceptors... )
|
||||
s.inproc = s.inproc.WithServerStreamInterceptor(si)
|
||||
|
||||
gopts := []grpc.ServerOption{
|
||||
|
Loading…
Reference in New Issue
Block a user