Compare commits
	
		
			2 Commits
		
	
	
		
			v4.1.8
			...
			6c9dbc77dd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6c9dbc77dd | |||
| 70f0ace92e | 
							
								
								
									
										117
									
								
								hooks/metadata/metadata.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								hooks/metadata/metadata.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/client" | ||||
| 	"go.unistack.org/micro/v4/metadata" | ||||
| 	"go.unistack.org/micro/v4/server" | ||||
| ) | ||||
|  | ||||
| type wrapper struct { | ||||
| 	keys []string | ||||
|  | ||||
| 	client.Client | ||||
| } | ||||
|  | ||||
| func NewClientWrapper(keys ...string) client.Wrapper { | ||||
| 	return func(c client.Client) client.Client { | ||||
| 		handler := &wrapper{ | ||||
| 			Client: c, | ||||
| 			keys:   keys, | ||||
| 		} | ||||
| 		return handler | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewClientCallWrapper(keys ...string) client.CallWrapper { | ||||
| 	return func(fn client.CallFunc) client.CallFunc { | ||||
| 		return func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error { | ||||
| 			if keys == nil { | ||||
| 				return fn(ctx, addr, req, rsp, opts) | ||||
| 			} | ||||
| 			if imd, iok := metadata.FromIncomingContext(ctx); iok && imd != nil { | ||||
| 				omd, ook := metadata.FromOutgoingContext(ctx) | ||||
| 				if !ook || omd == nil { | ||||
| 					omd = metadata.New(len(imd)) | ||||
| 				} | ||||
| 				for _, k := range keys { | ||||
| 					if v, ok := imd.Get(k); ok { | ||||
| 						omd.Add(k, v...) | ||||
| 					} | ||||
| 				} | ||||
| 				if !ook { | ||||
| 					ctx = metadata.NewOutgoingContext(ctx, omd) | ||||
| 				} | ||||
| 			} | ||||
| 			return fn(ctx, addr, req, rsp, opts) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { | ||||
| 	if w.keys == nil { | ||||
| 		return w.Client.Call(ctx, req, rsp, opts...) | ||||
| 	} | ||||
| 	if imd, iok := metadata.FromIncomingContext(ctx); iok && imd != nil { | ||||
| 		omd, ook := metadata.FromOutgoingContext(ctx) | ||||
| 		if !ook || omd == nil { | ||||
| 			omd = metadata.New(len(imd)) | ||||
| 		} | ||||
| 		for _, k := range w.keys { | ||||
| 			if v, ok := imd.Get(k); ok { | ||||
| 				omd.Add(k, v...) | ||||
| 			} | ||||
| 		} | ||||
| 		if !ook { | ||||
| 			ctx = metadata.NewOutgoingContext(ctx, omd) | ||||
| 		} | ||||
| 	} | ||||
| 	return w.Client.Call(ctx, req, rsp, opts...) | ||||
| } | ||||
|  | ||||
| func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { | ||||
| 	if w.keys == nil { | ||||
| 		return w.Client.Stream(ctx, req, opts...) | ||||
| 	} | ||||
| 	if imd, iok := metadata.FromIncomingContext(ctx); iok && imd != nil { | ||||
| 		omd, ook := metadata.FromOutgoingContext(ctx) | ||||
| 		if !ook || omd == nil { | ||||
| 			omd = metadata.New(len(imd)) | ||||
| 		} | ||||
| 		for _, k := range w.keys { | ||||
| 			if v, ok := imd.Get(k); ok { | ||||
| 				omd.Add(k, v...) | ||||
| 			} | ||||
| 		} | ||||
| 		if !ook { | ||||
| 			ctx = metadata.NewOutgoingContext(ctx, omd) | ||||
| 		} | ||||
| 	} | ||||
| 	return w.Client.Stream(ctx, req, opts...) | ||||
| } | ||||
|  | ||||
| func NewServerHandlerWrapper(keys ...string) server.HandlerWrapper { | ||||
| 	return func(fn server.HandlerFunc) server.HandlerFunc { | ||||
| 		return func(ctx context.Context, req server.Request, rsp interface{}) error { | ||||
| 			if keys == nil { | ||||
| 				return fn(ctx, req, rsp) | ||||
| 			} | ||||
| 			if imd, iok := metadata.FromIncomingContext(ctx); iok && imd != nil { | ||||
| 				omd, ook := metadata.FromOutgoingContext(ctx) | ||||
| 				if !ook || omd == nil { | ||||
| 					omd = metadata.New(len(imd)) | ||||
| 				} | ||||
| 				for _, k := range keys { | ||||
| 					if v, ok := imd.Get(k); ok { | ||||
| 						omd.Add(k, v...) | ||||
| 					} | ||||
| 				} | ||||
| 				if !ook { | ||||
| 					ctx = metadata.NewOutgoingContext(ctx, omd) | ||||
| 				} | ||||
| 			} | ||||
| 			return fn(ctx, req, rsp) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										63
									
								
								hooks/recovery/recovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								hooks/recovery/recovery.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| package recovery | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/errors" | ||||
| 	"go.unistack.org/micro/v4/server" | ||||
| ) | ||||
|  | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	options := Options{ | ||||
| 		ServerHandlerFn: DefaultServerHandlerFn, | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| 	return options | ||||
| } | ||||
|  | ||||
| type Options struct { | ||||
| 	ServerHandlerFn func(context.Context, server.Request, interface{}, error) error | ||||
| } | ||||
|  | ||||
| type Option func(*Options) | ||||
|  | ||||
| func ServerHandlerFunc(fn func(context.Context, server.Request, interface{}, error) error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ServerHandlerFn = fn | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var DefaultServerHandlerFn = func(ctx context.Context, req server.Request, rsp interface{}, err error) error { | ||||
| 	return errors.BadRequest("", "%v", err) | ||||
| } | ||||
|  | ||||
| var Hook = NewHook() | ||||
|  | ||||
| type hook struct { | ||||
| 	opts Options | ||||
| } | ||||
|  | ||||
| func NewHook(opts ...Option) *hook { | ||||
| 	return &hook{opts: NewOptions(opts...)} | ||||
| } | ||||
|  | ||||
| func (w *hook) ServerHandler(next server.FuncHandler) server.FuncHandler { | ||||
| 	return func(ctx context.Context, req server.Request, rsp interface{}) (err error) { | ||||
| 		defer func() { | ||||
| 			r := recover() | ||||
| 			switch verr := r.(type) { | ||||
| 			case nil: | ||||
| 				return | ||||
| 			case error: | ||||
| 				err = w.opts.ServerHandlerFn(ctx, req, rsp, verr) | ||||
| 			default: | ||||
| 				err = w.opts.ServerHandlerFn(ctx, req, rsp, fmt.Errorf("%v", r)) | ||||
| 			} | ||||
| 		}() | ||||
| 		err = next(ctx, req, rsp) | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										114
									
								
								hooks/requestid/requestid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								hooks/requestid/requestid.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| package requestid | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/textproto" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/client" | ||||
| 	"go.unistack.org/micro/v4/metadata" | ||||
| 	"go.unistack.org/micro/v4/server" | ||||
| 	"go.unistack.org/micro/v4/util/id" | ||||
| ) | ||||
|  | ||||
| type XRequestIDKey struct{} | ||||
|  | ||||
| // DefaultMetadataKey contains metadata key | ||||
| var DefaultMetadataKey = textproto.CanonicalMIMEHeaderKey("x-request-id") | ||||
|  | ||||
| // DefaultMetadataFunc wil be used if user not provide own func to fill metadata | ||||
| var DefaultMetadataFunc = func(ctx context.Context) (context.Context, error) { | ||||
| 	var xid string | ||||
|  | ||||
| 	cid, cok := ctx.Value(XRequestIDKey{}).(string) | ||||
| 	if cok && cid != "" { | ||||
| 		xid = cid | ||||
| 	} | ||||
|  | ||||
| 	imd, iok := metadata.FromIncomingContext(ctx) | ||||
| 	if !iok || imd == nil { | ||||
| 		imd = metadata.New(1) | ||||
| 		ctx = metadata.NewIncomingContext(ctx, imd) | ||||
| 	} | ||||
|  | ||||
| 	omd, ook := metadata.FromOutgoingContext(ctx) | ||||
| 	if !ook || omd == nil { | ||||
| 		omd = metadata.New(1) | ||||
| 		ctx = metadata.NewOutgoingContext(ctx, omd) | ||||
| 	} | ||||
|  | ||||
| 	if xid == "" { | ||||
| 		var ids []string | ||||
| 		if ids, iok = imd.Get(DefaultMetadataKey); iok { | ||||
| 			for i := range ids { | ||||
| 				if ids[i] != "" { | ||||
| 					xid = ids[i] | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if ids, ook = omd.Get(DefaultMetadataKey); ook { | ||||
| 			for i := range ids { | ||||
| 				if ids[i] != "" { | ||||
| 					xid = ids[i] | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if xid == "" { | ||||
| 		var err error | ||||
| 		xid, err = id.New() | ||||
| 		if err != nil { | ||||
| 			return ctx, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !cok { | ||||
| 		ctx = context.WithValue(ctx, XRequestIDKey{}, xid) | ||||
| 	} | ||||
|  | ||||
| 	if !iok { | ||||
| 		imd.Set(DefaultMetadataKey, xid) | ||||
| 	} | ||||
|  | ||||
| 	if !ook { | ||||
| 		omd.Set(DefaultMetadataKey, xid) | ||||
| 	} | ||||
|  | ||||
| 	return ctx, nil | ||||
| } | ||||
|  | ||||
| type hook struct{} | ||||
|  | ||||
| func NewHook() *hook { | ||||
| 	return &hook{} | ||||
| } | ||||
|  | ||||
| func (w *hook) ServerHandler(next server.FuncHandler) server.FuncHandler { | ||||
| 	return func(ctx context.Context, req server.Request, rsp interface{}) error { | ||||
| 		var err error | ||||
| 		if ctx, err = DefaultMetadataFunc(ctx); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return next(ctx, req, rsp) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *hook) ClientCall(next client.FuncCall) client.FuncCall { | ||||
| 	return func(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { | ||||
| 		var err error | ||||
| 		if ctx, err = DefaultMetadataFunc(ctx); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return next(ctx, req, rsp, opts...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *hook) ClientStream(next client.FuncStream) client.FuncStream { | ||||
| 	return func(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { | ||||
| 		var err error | ||||
| 		if ctx, err = DefaultMetadataFunc(ctx); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return next(ctx, req, opts...) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										33
									
								
								hooks/requestid/requestid_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								hooks/requestid/requestid_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| package requestid | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/metadata" | ||||
| ) | ||||
|  | ||||
| func TestDefaultMetadataFunc(t *testing.T) { | ||||
| 	ctx := context.TODO() | ||||
|  | ||||
| 	nctx, err := DefaultMetadataFunc(ctx) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("%v", err) | ||||
| 	} | ||||
|  | ||||
| 	imd, ok := metadata.FromIncomingContext(nctx) | ||||
| 	if !ok { | ||||
| 		t.Fatalf("md missing in incoming context") | ||||
| 	} | ||||
| 	omd, ok := metadata.FromOutgoingContext(nctx) | ||||
| 	if !ok { | ||||
| 		t.Fatalf("md missing in outgoing context") | ||||
| 	} | ||||
|  | ||||
| 	_, iok := imd.Get(DefaultMetadataKey) | ||||
| 	_, ook := omd.Get(DefaultMetadataKey) | ||||
|  | ||||
| 	if !iok || !ook { | ||||
| 		t.Fatalf("missing metadata key value") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										133
									
								
								hooks/validator/validator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								hooks/validator/validator.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| package validator | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/client" | ||||
| 	"go.unistack.org/micro/v4/errors" | ||||
| 	"go.unistack.org/micro/v4/server" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	DefaultClientErrorFunc = func(req client.Request, rsp interface{}, err error) error { | ||||
| 		if rsp != nil { | ||||
| 			return errors.BadGateway(req.Service(), "%v", err) | ||||
| 		} | ||||
| 		return errors.BadRequest(req.Service(), "%v", err) | ||||
| 	} | ||||
|  | ||||
| 	DefaultServerErrorFunc = func(req server.Request, rsp interface{}, err error) error { | ||||
| 		if rsp != nil { | ||||
| 			return errors.BadGateway(req.Service(), "%v", err) | ||||
| 		} | ||||
| 		return errors.BadRequest(req.Service(), "%v", err) | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	ClientErrorFunc func(client.Request, interface{}, error) error | ||||
| 	ServerErrorFunc func(server.Request, interface{}, error) error | ||||
| ) | ||||
|  | ||||
| // Options struct holds wrapper options | ||||
| type Options struct { | ||||
| 	ClientErrorFn          ClientErrorFunc | ||||
| 	ServerErrorFn          ServerErrorFunc | ||||
| 	ClientValidateResponse bool | ||||
| 	ServerValidateResponse bool | ||||
| } | ||||
|  | ||||
| // Option func signature | ||||
| type Option func(*Options) | ||||
|  | ||||
| func ClientValidateResponse(b bool) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ClientValidateResponse = b | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ServerValidateResponse(b bool) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ClientValidateResponse = b | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ClientReqErrorFn(fn ClientErrorFunc) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ClientErrorFn = fn | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ServerErrorFn(fn ServerErrorFunc) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ServerErrorFn = fn | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	options := Options{ | ||||
| 		ClientErrorFn: DefaultClientErrorFunc, | ||||
| 		ServerErrorFn: DefaultServerErrorFunc, | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| 	return options | ||||
| } | ||||
|  | ||||
| func NewHook(opts ...Option) *hook { | ||||
| 	return &hook{opts: NewOptions(opts...)} | ||||
| } | ||||
|  | ||||
| type validator interface { | ||||
| 	Validate() error | ||||
| } | ||||
|  | ||||
| type hook struct { | ||||
| 	opts Options | ||||
| } | ||||
|  | ||||
| func (w *hook) ClientCall(next client.FuncCall) client.FuncCall { | ||||
| 	return func(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { | ||||
| 		if v, ok := req.Body().(validator); ok { | ||||
| 			if err := v.Validate(); err != nil { | ||||
| 				return w.opts.ClientErrorFn(req, nil, err) | ||||
| 			} | ||||
| 		} | ||||
| 		err := next(ctx, req, rsp, opts...) | ||||
| 		if v, ok := rsp.(validator); ok && w.opts.ClientValidateResponse { | ||||
| 			if verr := v.Validate(); verr != nil { | ||||
| 				return w.opts.ClientErrorFn(req, rsp, verr) | ||||
| 			} | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *hook) ClientStream(next client.FuncStream) client.FuncStream { | ||||
| 	return func(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { | ||||
| 		if v, ok := req.Body().(validator); ok { | ||||
| 			if err := v.Validate(); err != nil { | ||||
| 				return nil, w.opts.ClientErrorFn(req, nil, err) | ||||
| 			} | ||||
| 		} | ||||
| 		return next(ctx, req, opts...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *hook) ServerHandler(next server.FuncHandler) server.FuncHandler { | ||||
| 	return func(ctx context.Context, req server.Request, rsp interface{}) error { | ||||
| 		if v, ok := req.Body().(validator); ok { | ||||
| 			if err := v.Validate(); err != nil { | ||||
| 				return w.opts.ServerErrorFn(req, nil, err) | ||||
| 			} | ||||
| 		} | ||||
| 		err := next(ctx, req, rsp) | ||||
| 		if v, ok := rsp.(validator); ok && w.opts.ServerValidateResponse { | ||||
| 			if verr := v.Validate(); verr != nil { | ||||
| 				return w.opts.ServerErrorFn(req, rsp, verr) | ||||
| 			} | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user