broker refactor
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
c9066e0455
commit
c5e6d4cddc
@ -20,6 +20,8 @@ var (
|
|||||||
ErrDisconnected = errors.New("broker disconnected")
|
ErrDisconnected = errors.New("broker disconnected")
|
||||||
// ErrInvalidMessage returns when invalid Message passed
|
// ErrInvalidMessage returns when invalid Message passed
|
||||||
ErrInvalidMessage = errors.New("invalid message")
|
ErrInvalidMessage = errors.New("invalid message")
|
||||||
|
// ErrInvalidHandler returns when subscriber passed to Subscribe
|
||||||
|
ErrInvalidHandler = errors.New("invalid handler")
|
||||||
// DefaultGracefulTimeout
|
// DefaultGracefulTimeout
|
||||||
DefaultGracefulTimeout = 5 * time.Second
|
DefaultGracefulTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
@ -87,9 +89,3 @@ type Subscriber interface {
|
|||||||
// Unsubscribe from topic
|
// Unsubscribe from topic
|
||||||
Unsubscribe(ctx context.Context) error
|
Unsubscribe(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageHandler func signature for single message processing
|
|
||||||
type MessageHandler func(Message) error
|
|
||||||
|
|
||||||
// MessagesHandler func signature for batch message processing
|
|
||||||
type MessagesHandler func([]Message) error
|
|
||||||
|
@ -2,6 +2,7 @@ package broker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/broker"
|
"go.unistack.org/micro/v4/broker"
|
||||||
@ -34,6 +35,30 @@ type memoryMessage struct {
|
|||||||
opts broker.PublishOptions
|
opts broker.PublishOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Ack() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Body() []byte {
|
||||||
|
return m.body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Header() metadata.Metadata {
|
||||||
|
return m.hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Context() context.Context {
|
||||||
|
return m.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Topic() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryMessage) Unmarshal(dst interface{}, opts ...codec.Option) error {
|
||||||
|
return m.c.Unmarshal(m.body, dst)
|
||||||
|
}
|
||||||
|
|
||||||
type Subscriber struct {
|
type Subscriber struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
exit chan bool
|
exit chan bool
|
||||||
@ -43,25 +68,38 @@ type Subscriber struct {
|
|||||||
opts broker.SubscribeOptions
|
opts broker.SubscribeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Options() broker.Options {
|
func (b *Broker) newCodec(ct string) (codec.Codec, error) {
|
||||||
return m.opts
|
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||||
|
ct = ct[:idx]
|
||||||
|
}
|
||||||
|
b.RLock()
|
||||||
|
c, ok := b.opts.Codecs[ct]
|
||||||
|
b.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, codec.ErrUnknownContentType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Address() string {
|
func (b *Broker) Options() broker.Options {
|
||||||
return m.addr
|
return b.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Connect(ctx context.Context) error {
|
func (b *Broker) Address() string {
|
||||||
|
return b.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Broker) Connect(ctx context.Context) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Lock()
|
b.Lock()
|
||||||
defer m.Unlock()
|
defer b.Unlock()
|
||||||
|
|
||||||
if m.connected {
|
if b.connected {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,65 +113,79 @@ func (m *Broker) Connect(ctx context.Context) error {
|
|||||||
// set addr with port
|
// set addr with port
|
||||||
addr = mnet.HostPort(addr, 10000+i)
|
addr = mnet.HostPort(addr, 10000+i)
|
||||||
|
|
||||||
m.addr = addr
|
b.addr = addr
|
||||||
m.connected = true
|
b.connected = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Disconnect(ctx context.Context) error {
|
func (b *Broker) Disconnect(ctx context.Context) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Lock()
|
b.Lock()
|
||||||
defer m.Unlock()
|
defer b.Unlock()
|
||||||
|
|
||||||
if !m.connected {
|
if !b.connected {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.connected = false
|
b.connected = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Init(opts ...broker.Option) error {
|
func (b *Broker) Init(opts ...broker.Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&m.opts)
|
o(&b.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.funcPublish = m.fnPublish
|
b.funcPublish = b.fnPublish
|
||||||
m.funcSubscribe = m.fnSubscribe
|
b.funcSubscribe = b.fnSubscribe
|
||||||
|
|
||||||
m.opts.Hooks.EachPrev(func(hook options.Hook) {
|
b.opts.Hooks.EachPrev(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case broker.HookPublish:
|
case broker.HookPublish:
|
||||||
m.funcPublish = h(m.funcPublish)
|
b.funcPublish = h(b.funcPublish)
|
||||||
case broker.HookSubscribe:
|
case broker.HookSubscribe:
|
||||||
m.funcSubscribe = h(m.funcSubscribe)
|
b.funcSubscribe = h(b.funcSubscribe)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Publish(ctx context.Context, topic string, messages ...broker.Message) error {
|
func (b *Broker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...broker.PublishOption) (broker.Message, error) {
|
||||||
return m.funcPublish(ctx, topic, messages...)
|
options := broker.NewPublishOptions(opts...)
|
||||||
|
m := &memoryMessage{ctx: ctx, hdr: hdr, opts: options}
|
||||||
|
c, err := b.newCodec(m.opts.ContentType)
|
||||||
|
if err == nil {
|
||||||
|
m.body, err = c.Marshal(body)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) fnPublish(ctx context.Context, topic string, messages ...broker.Message) error {
|
func (b *Broker) Publish(ctx context.Context, topic string, messages ...broker.Message) error {
|
||||||
return m.publish(ctx, topic, messages...)
|
return b.funcPublish(ctx, topic, messages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error {
|
func (b *Broker) fnPublish(ctx context.Context, topic string, messages ...broker.Message) error {
|
||||||
m.RLock()
|
return b.publish(ctx, topic, messages...)
|
||||||
if !m.connected {
|
}
|
||||||
m.RUnlock()
|
|
||||||
|
func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error {
|
||||||
|
b.RLock()
|
||||||
|
if !b.connected {
|
||||||
|
b.RUnlock()
|
||||||
return broker.ErrNotConnected
|
return broker.ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
b.RUnlock()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@ -141,9 +193,9 @@ func (m *Broker) publish(ctx context.Context, topic string, messages ...broker.M
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
m.RLock()
|
b.RLock()
|
||||||
subs, ok := m.subscribers[topic]
|
subs, ok := b.subscribers[topic]
|
||||||
m.RUnlock()
|
b.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -152,24 +204,28 @@ func (m *Broker) publish(ctx context.Context, topic string, messages ...broker.M
|
|||||||
|
|
||||||
for _, sub := range subs {
|
for _, sub := range subs {
|
||||||
switch s := sub.handler.(type) {
|
switch s := sub.handler.(type) {
|
||||||
case broker.MessageHandler:
|
default:
|
||||||
|
if b.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
b.opts.Logger.Error(ctx, "broker handler error", broker.ErrInvalidHandler)
|
||||||
|
}
|
||||||
|
case func(broker.Message) error:
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
if err = s(message); err == nil && sub.opts.AutoAck {
|
if err = s(message); err == nil && sub.opts.AutoAck {
|
||||||
err = message.Ack()
|
err = message.Ack()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if m.opts.Logger.V(logger.ErrorLevel) {
|
if b.opts.Logger.V(logger.ErrorLevel) {
|
||||||
m.opts.Logger.Error(m.opts.Context, "broker handler error", err)
|
b.opts.Logger.Error(ctx, "broker handler error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case broker.MessagesHandler:
|
case func([]broker.Message) error:
|
||||||
if err = s(messages); err == nil && sub.opts.AutoAck {
|
if err = s(messages); err == nil && sub.opts.AutoAck {
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
err = message.Ack()
|
err = message.Ack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if m.opts.Logger.V(logger.ErrorLevel) {
|
if b.opts.Logger.V(logger.ErrorLevel) {
|
||||||
m.opts.Logger.Error(m.opts.Context, "broker handler error", err)
|
b.opts.Logger.Error(ctx, "broker handler error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,17 +236,21 @@ func (m *Broker) publish(ctx context.Context, topic string, messages ...broker.M
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (b *Broker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
return m.funcSubscribe(ctx, topic, handler, opts...)
|
return b.funcSubscribe(ctx, topic, handler, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) fnSubscribe(ctx context.Context, topic string, handler interface{}, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (b *Broker) fnSubscribe(ctx context.Context, topic string, handler interface{}, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
m.RLock()
|
if err := broker.IsValidHandler(handler); err != nil {
|
||||||
if !m.connected {
|
return nil, err
|
||||||
m.RUnlock()
|
}
|
||||||
|
|
||||||
|
b.RLock()
|
||||||
|
if !b.connected {
|
||||||
|
b.RUnlock()
|
||||||
return nil, broker.ErrNotConnected
|
return nil, broker.ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
b.RUnlock()
|
||||||
|
|
||||||
sid, err := id.New()
|
sid, err := id.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -208,63 +268,47 @@ func (m *Broker) fnSubscribe(ctx context.Context, topic string, handler interfac
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Lock()
|
b.Lock()
|
||||||
m.subscribers[topic] = append(m.subscribers[topic], sub)
|
b.subscribers[topic] = append(b.subscribers[topic], sub)
|
||||||
m.Unlock()
|
b.Unlock()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-sub.exit
|
<-sub.exit
|
||||||
m.Lock()
|
b.Lock()
|
||||||
newSubscribers := make([]*Subscriber, 0, len(m.subscribers)-1)
|
newSubscribers := make([]*Subscriber, 0, len(b.subscribers)-1)
|
||||||
for _, sb := range m.subscribers[topic] {
|
for _, sb := range b.subscribers[topic] {
|
||||||
if sb.id == sub.id {
|
if sb.id == sub.id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newSubscribers = append(newSubscribers, sb)
|
newSubscribers = append(newSubscribers, sb)
|
||||||
}
|
}
|
||||||
m.subscribers[topic] = newSubscribers
|
b.subscribers[topic] = newSubscribers
|
||||||
m.Unlock()
|
b.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return sub, nil
|
return sub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) String() string {
|
func (b *Broker) String() string {
|
||||||
return "memory"
|
return "memory"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Name() string {
|
func (b *Broker) Name() string {
|
||||||
return m.opts.Name
|
return b.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Live() bool {
|
func (b *Broker) Live() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Ready() bool {
|
func (b *Broker) Ready() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Broker) Health() bool {
|
func (b *Broker) Health() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryMessage) Topic() string {
|
|
||||||
return m.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryMessage) Body() []byte {
|
|
||||||
return m.body
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryMessage) Ack() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryMessage) Context() context.Context {
|
|
||||||
return m.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Subscriber) Options() broker.SubscribeOptions {
|
func (m *Subscriber) Options() broker.SubscribeOptions {
|
||||||
return m.opts
|
return m.opts
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,23 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/atomic"
|
||||||
"go.unistack.org/micro/v4/broker"
|
"go.unistack.org/micro/v4/broker"
|
||||||
|
"go.unistack.org/micro/v4/codec"
|
||||||
"go.unistack.org/micro/v4/metadata"
|
"go.unistack.org/micro/v4/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type hldr struct {
|
||||||
|
c atomic.Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hldr) Handler(m broker.Message) error {
|
||||||
|
h.c.Add(1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestMemoryBroker(t *testing.T) {
|
func TestMemoryBroker(t *testing.T) {
|
||||||
b := NewBroker()
|
b := NewBroker(broker.Codec("application/octet-stream", codec.NewCodec()))
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if err := b.Init(); err != nil {
|
if err := b.Init(); err != nil {
|
||||||
@ -22,28 +33,27 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
topic := "test"
|
topic := "test"
|
||||||
count := 10
|
count := int64(10)
|
||||||
|
|
||||||
fn := func(_ broker.Message) error {
|
h := &hldr{}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sub, err := b.Subscribe(ctx, topic, fn)
|
sub, err := b.Subscribe(ctx, topic, h.Handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error subscribing %v", err)
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs := make([]*broker.Message, 0, count)
|
for i := int64(0); i < count; i++ {
|
||||||
for i := 0; i < count; i++ {
|
message, err := b.NewMessage(ctx,
|
||||||
message, err := b.NewMessage(ctx, metadata.Pairs()
|
metadata.Pairs(
|
||||||
Header: map[string]string{
|
"foo", "bar",
|
||||||
metadata.HeaderTopic: topic,
|
"id", fmt.Sprintf("%d", i),
|
||||||
"foo": "bar",
|
),
|
||||||
"id": fmt.Sprintf("%d", i),
|
|
||||||
},
|
|
||||||
[]byte(`"hello world"`),
|
[]byte(`"hello world"`),
|
||||||
|
broker.PublishContentType("application/octet-stream"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
msgs = append(msgs, message)
|
|
||||||
|
|
||||||
if err := b.Publish(ctx, topic, message); err != nil {
|
if err := b.Publish(ctx, topic, message); err != nil {
|
||||||
t.Fatalf("Unexpected error publishing %d err: %v", i, err)
|
t.Fatalf("Unexpected error publishing %d err: %v", i, err)
|
||||||
@ -57,4 +67,8 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
if err := b.Disconnect(ctx); err != nil {
|
if err := b.Disconnect(ctx); err != nil {
|
||||||
t.Fatalf("Unexpected connect error %v", err)
|
t.Fatalf("Unexpected connect error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.c.Load() != count {
|
||||||
|
t.Fatal("invalid messages count received")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,6 @@ func (m *noopMessage) Context() context.Context {
|
|||||||
return m.ctx
|
return m.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *noopMessage) Error() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *noopMessage) Topic() string {
|
func (m *noopMessage) Topic() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,8 @@ func Addrs(addrs ...string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Codecs sets the codec used for encoding/decoding messages
|
// Codec sets the codec used for encoding/decoding messages
|
||||||
func Codecs(ct string, c codec.Codec) Option {
|
func Codec(ct string, c codec.Codec) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Codecs[ct] = c
|
o.Codecs[ct] = c
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
//go:build ignore
|
package broker
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -10,7 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
subSig = "func(context.Context, interface{}) error"
|
messageSig = "func(broker.Message) error"
|
||||||
|
messagesSig = "func([]broker.Message) error"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Precompute the reflect type for error. Can't use error directly
|
// Precompute the reflect type for error. Can't use error directly
|
||||||
@ -33,31 +32,31 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
|
|||||||
return isExported(t.Name()) || t.PkgPath() == ""
|
return isExported(t.Name()) || t.PkgPath() == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSubscriber func signature
|
// IsValidHandler func signature
|
||||||
func ValidateSubscriber(sub Subscriber) error {
|
func IsValidHandler(sub interface{}) error {
|
||||||
typ := reflect.TypeOf(sub.Subscriber())
|
typ := reflect.TypeOf(sub)
|
||||||
var argType reflect.Type
|
var argType reflect.Type
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
name := "Func"
|
name := "Func"
|
||||||
switch typ.NumIn() {
|
switch typ.NumIn() {
|
||||||
case 2:
|
case 1:
|
||||||
argType = typ.In(1)
|
argType = typ.In(0)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig)
|
return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), messageSig)
|
||||||
}
|
}
|
||||||
if !isExportedOrBuiltinType(argType) {
|
if !isExportedOrBuiltinType(argType) {
|
||||||
return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
|
return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
|
||||||
}
|
}
|
||||||
if typ.NumOut() != 1 {
|
if typ.NumOut() != 1 {
|
||||||
return fmt.Errorf("subscriber %v has wrong number of return values: %v require signature %s",
|
return fmt.Errorf("subscriber %v has wrong number of return values: %v require signature %s",
|
||||||
name, typ.NumOut(), subSig)
|
name, typ.NumOut(), messageSig)
|
||||||
}
|
}
|
||||||
if returnType := typ.Out(0); returnType != typeOfError {
|
if returnType := typ.Out(0); returnType != typeOfError {
|
||||||
return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
|
return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
hdlr := reflect.ValueOf(sub.Subscriber())
|
hdlr := reflect.ValueOf(sub)
|
||||||
name := reflect.Indirect(hdlr).Type().Name()
|
name := reflect.Indirect(hdlr).Type().Name()
|
||||||
|
|
||||||
for m := 0; m < typ.NumMethod(); m++ {
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
@ -67,7 +66,7 @@ func ValidateSubscriber(sub Subscriber) error {
|
|||||||
argType = method.Type.In(2)
|
argType = method.Type.In(2)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
|
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
|
||||||
name, method.Name, method.Type.NumIn(), subSig)
|
name, method.Name, method.Type.NumIn(), messageSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isExportedOrBuiltinType(argType) {
|
if !isExportedOrBuiltinType(argType) {
|
||||||
@ -76,7 +75,7 @@ func ValidateSubscriber(sub Subscriber) error {
|
|||||||
if method.Type.NumOut() != 1 {
|
if method.Type.NumOut() != 1 {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"subscriber %v.%v has wrong number of return values: %v require signature %s",
|
"subscriber %v.%v has wrong number of return values: %v require signature %s",
|
||||||
name, method.Name, method.Type.NumOut(), subSig)
|
name, method.Name, method.Type.NumOut(), messageSig)
|
||||||
}
|
}
|
||||||
if returnType := method.Type.Out(0); returnType != typeOfError {
|
if returnType := method.Type.Out(0); returnType != typeOfError {
|
||||||
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
|
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
|
||||||
|
@ -412,9 +412,10 @@ func Test_WithContextAttrFunc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
attrs := make([]interface{}, 0, 10)
|
attrs := make([]interface{}, 0, 10)
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
switch k {
|
key := strings.ToLower(k)
|
||||||
case "X-Request-Id", "Phone", "External-Id", "Source-Service", "X-App-Install-Id", "Client-Id", "Client-Ip":
|
switch key {
|
||||||
attrs = append(attrs, strings.ToLower(k), v)
|
case "x-request-id", "phone", "external-Id", "source-service", "x-app-install-id", "client-id", "client-ip":
|
||||||
|
attrs = append(attrs, key, v[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return attrs
|
return attrs
|
||||||
|
@ -1,181 +0,0 @@
|
|||||||
//go:build !exclude
|
|
||||||
|
|
||||||
// Package metadata is a way of defining message headers
|
|
||||||
package metadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
mdIncomingKey struct{}
|
|
||||||
mdOutgoingKey struct{}
|
|
||||||
mdKey struct{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromIncomingContext returns metadata from incoming ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens
|
|
||||||
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata)
|
|
||||||
if !ok || md.md == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return md.md, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustIncomingContext returns metadata from incoming ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens.
|
|
||||||
// If metadata not exists panics.
|
|
||||||
func MustIncomingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromOutgoingContext returns metadata from outgoing ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens
|
|
||||||
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata)
|
|
||||||
if !ok || md.md == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return md.md, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustOutgoingContext returns metadata from outgoing ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens.
|
|
||||||
// If metadata not exists panics.
|
|
||||||
func MustOutgoingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromContext returns metadata from the given context
|
|
||||||
// returned metadata shoud not be modified or race condition happens
|
|
||||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
md, ok := ctx.Value(mdKey{}).(*rawMetadata)
|
|
||||||
if !ok || md.md == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return md.md, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustContext returns metadata from the given context
|
|
||||||
// returned metadata shoud not be modified or race condition happens
|
|
||||||
func MustContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext creates a new context with the given metadata
|
|
||||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, mdKey{}, &rawMetadata{md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutgoingContext modify outgoing context with given metadata
|
|
||||||
func SetOutgoingContext(ctx context.Context, md Metadata) bool {
|
|
||||||
if ctx == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if omd, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata); ok {
|
|
||||||
omd.md = md
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIncomingContext modify incoming context with given metadata
|
|
||||||
func SetIncomingContext(ctx context.Context, md Metadata) bool {
|
|
||||||
if ctx == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if omd, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata); ok {
|
|
||||||
omd.md = md
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIncomingContext creates a new context with incoming metadata attached
|
|
||||||
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOutgoingContext creates a new context with outcoming metadata attached
|
|
||||||
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendOutgoingContext apends new md to context
|
|
||||||
func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
md, ok := Pairs(kv...)
|
|
||||||
if !ok {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
omd, ok := FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return NewOutgoingContext(ctx, md)
|
|
||||||
}
|
|
||||||
for k, v := range md {
|
|
||||||
omd.Set(k, v)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendIncomingContext apends new md to context
|
|
||||||
func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
md, ok := Pairs(kv...)
|
|
||||||
if !ok {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
omd, ok := FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return NewIncomingContext(ctx, md)
|
|
||||||
}
|
|
||||||
for k, v := range md {
|
|
||||||
omd.Set(k, v)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendContext apends new md to context
|
|
||||||
func AppendContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
md, ok := Pairs(kv...)
|
|
||||||
if !ok {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
omd, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return NewContext(ctx, md)
|
|
||||||
}
|
|
||||||
for k, v := range md {
|
|
||||||
omd.Set(k, v)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
package metadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFromNilContext(t *testing.T) {
|
|
||||||
// nolint: staticcheck
|
|
||||||
c, ok := FromContext(nil)
|
|
||||||
if ok || c != nil {
|
|
||||||
t.Fatal("FromContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewNilContext(t *testing.T) {
|
|
||||||
// nolint: staticcheck
|
|
||||||
ctx := NewContext(nil, New(0))
|
|
||||||
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("NewContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromContext(t *testing.T) {
|
|
||||||
ctx := context.WithValue(context.TODO(), mdKey{}, &rawMetadata{New(0)})
|
|
||||||
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("FromContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewContext(t *testing.T) {
|
|
||||||
ctx := NewContext(context.TODO(), New(0))
|
|
||||||
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("NewContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromIncomingContext(t *testing.T) {
|
|
||||||
ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{New(0)})
|
|
||||||
|
|
||||||
c, ok := FromIncomingContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("FromIncomingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromOutgoingContext(t *testing.T) {
|
|
||||||
ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{New(0)})
|
|
||||||
|
|
||||||
c, ok := FromOutgoingContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("FromOutgoingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetIncomingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key", "val")
|
|
||||||
ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{})
|
|
||||||
if !SetIncomingContext(ctx, md) {
|
|
||||||
t.Fatal("SetIncomingContext not works")
|
|
||||||
}
|
|
||||||
md, ok := FromIncomingContext(ctx)
|
|
||||||
if md == nil || !ok {
|
|
||||||
t.Fatal("SetIncomingContext not works")
|
|
||||||
} else if v, ok := md.Get("key"); !ok || v != "val" {
|
|
||||||
t.Fatal("SetIncomingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetOutgoingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key", "val")
|
|
||||||
ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{})
|
|
||||||
if !SetOutgoingContext(ctx, md) {
|
|
||||||
t.Fatal("SetOutgoingContext not works")
|
|
||||||
}
|
|
||||||
md, ok := FromOutgoingContext(ctx)
|
|
||||||
if md == nil || !ok {
|
|
||||||
t.Fatal("SetOutgoingContext not works")
|
|
||||||
} else if v, ok := md.Get("key"); !ok || v != "val" {
|
|
||||||
t.Fatal("SetOutgoingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewIncomingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key", "val")
|
|
||||||
ctx := NewIncomingContext(context.TODO(), md)
|
|
||||||
|
|
||||||
c, ok := FromIncomingContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("NewIncomingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewOutgoingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key", "val")
|
|
||||||
ctx := NewOutgoingContext(context.TODO(), md)
|
|
||||||
|
|
||||||
c, ok := FromOutgoingContext(ctx)
|
|
||||||
if c == nil || !ok {
|
|
||||||
t.Fatal("NewOutgoingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendIncomingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key1", "val1")
|
|
||||||
ctx := AppendIncomingContext(context.TODO(), "key2", "val2")
|
|
||||||
|
|
||||||
nmd, ok := FromIncomingContext(ctx)
|
|
||||||
if nmd == nil || !ok {
|
|
||||||
t.Fatal("AppendIncomingContext not works")
|
|
||||||
}
|
|
||||||
if v, ok := nmd.Get("key2"); !ok || v != "val2" {
|
|
||||||
t.Fatal("AppendIncomingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendOutgoingContext(t *testing.T) {
|
|
||||||
md := New(1)
|
|
||||||
md.Set("key1", "val1")
|
|
||||||
ctx := AppendOutgoingContext(context.TODO(), "key2", "val2")
|
|
||||||
|
|
||||||
nmd, ok := FromOutgoingContext(ctx)
|
|
||||||
if nmd == nil || !ok {
|
|
||||||
t.Fatal("AppendOutgoingContext not works")
|
|
||||||
}
|
|
||||||
if v, ok := nmd.Get("key2"); !ok || v != "val2" {
|
|
||||||
t.Fatal("AppendOutgoingContext not works")
|
|
||||||
}
|
|
||||||
}
|
|
19
metadata/headers.go
Normal file
19
metadata/headers.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Package metadata is a way of defining message headers
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
// HeaderTopic is the header name that contains topic name
|
||||||
|
HeaderTopic = "Micro-Topic"
|
||||||
|
// HeaderContentType specifies content type of message
|
||||||
|
HeaderContentType = "Content-Type"
|
||||||
|
// HeaderEndpoint specifies endpoint in service
|
||||||
|
HeaderEndpoint = "Micro-Endpoint"
|
||||||
|
// HeaderService specifies service
|
||||||
|
HeaderService = "Micro-Service"
|
||||||
|
// HeaderTimeout specifies timeout of operation
|
||||||
|
HeaderTimeout = "Micro-Timeout"
|
||||||
|
// HeaderAuthorization specifies Authorization header
|
||||||
|
HeaderAuthorization = "Authorization"
|
||||||
|
// HeaderXRequestID specifies request id
|
||||||
|
HeaderXRequestID = "X-Request-Id"
|
||||||
|
)
|
@ -1,43 +1,461 @@
|
|||||||
//go:build !exclude
|
|
||||||
|
|
||||||
// Package metadata is a way of defining message headers
|
|
||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// HeaderTopic is the header name that contains topic name
|
|
||||||
HeaderTopic = "Micro-Topic"
|
|
||||||
// HeaderContentType specifies content type of message
|
|
||||||
HeaderContentType = "Content-Type"
|
|
||||||
// HeaderEndpoint specifies endpoint in service
|
|
||||||
HeaderEndpoint = "Micro-Endpoint"
|
|
||||||
// HeaderService specifies service
|
|
||||||
HeaderService = "Micro-Service"
|
|
||||||
// HeaderTimeout specifies timeout of operation
|
|
||||||
HeaderTimeout = "Micro-Timeout"
|
|
||||||
// HeaderAuthorization specifies Authorization header
|
|
||||||
HeaderAuthorization = "Authorization"
|
|
||||||
// HeaderXRequestID specifies request id
|
|
||||||
HeaderXRequestID = "X-Request-Id"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Metadata is our way of representing request headers internally.
|
|
||||||
// They're used at the RPC level and translate back and forth
|
|
||||||
// from Transport headers.
|
|
||||||
type Metadata map[string]string
|
|
||||||
|
|
||||||
type rawMetadata struct {
|
|
||||||
md Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultMetadataSize used when need to init new Metadata
|
// defaultMetadataSize used when need to init new Metadata
|
||||||
var defaultMetadataSize = 2
|
var defaultMetadataSize = 2
|
||||||
|
|
||||||
|
// Metadata is a mapping from metadata keys to values. Users should use the following
|
||||||
|
// two convenience functions New and Pairs to generate Metadata.
|
||||||
|
type Metadata map[string][]string
|
||||||
|
|
||||||
|
// New creates an zero Metadata.
|
||||||
|
func New(l int) Metadata {
|
||||||
|
if l == 0 {
|
||||||
|
l = defaultMetadataSize
|
||||||
|
}
|
||||||
|
md := make(Metadata, l)
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithMetadata creates an Metadata from a given key-value map.
|
||||||
|
func NewWithMetadata(m map[string]string) Metadata {
|
||||||
|
md := make(Metadata, len(m))
|
||||||
|
for key, val := range m {
|
||||||
|
md[key] = append(md[key], val)
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pairs returns an Metadata formed by the mapping of key, value ...
|
||||||
|
// Pairs panics if len(kv) is odd.
|
||||||
|
func Pairs(kv ...string) Metadata {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md := make(Metadata, len(kv)/2)
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
md[kv[i]] = append(md[kv[i]], kv[i+1])
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in Metadata.
|
||||||
|
func (md Metadata) Len() int {
|
||||||
|
return len(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of Metadata.
|
||||||
|
func Copy(src Metadata) Metadata {
|
||||||
|
out := make(Metadata, len(src))
|
||||||
|
for k, v := range src {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of Metadata.
|
||||||
|
func (md Metadata) Copy() Metadata {
|
||||||
|
out := make(Metadata, len(md))
|
||||||
|
for k, v := range md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsHTTP1 returns a copy of Metadata
|
||||||
|
// with CanonicalMIMEHeaderKey.
|
||||||
|
func (md Metadata) AsHTTP1() map[string][]string {
|
||||||
|
out := make(map[string][]string, len(md))
|
||||||
|
for k, v := range md {
|
||||||
|
out[textproto.CanonicalMIMEHeaderKey(k)] = copyOf(v)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsHTTP1 returns a copy of Metadata
|
||||||
|
// with strings.ToLower.
|
||||||
|
func (md Metadata) AsHTTP2() map[string][]string {
|
||||||
|
out := make(map[string][]string, len(md))
|
||||||
|
for k, v := range md {
|
||||||
|
out[strings.ToLower(k)] = copyOf(v)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyTo copies Metadata to out.
|
||||||
|
func (md Metadata) CopyTo(out Metadata) {
|
||||||
|
for k, v := range md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get obtains the values for a given key.
|
||||||
|
func (md Metadata) MustGet(k string) []string {
|
||||||
|
v, ok := md.Get(k)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata key")
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get obtains the values for a given key.
|
||||||
|
func (md Metadata) Get(k string) ([]string, bool) {
|
||||||
|
v, ok := md[k]
|
||||||
|
if !ok {
|
||||||
|
v, ok = md[strings.ToLower(k)]
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
v, ok = md[textproto.CanonicalMIMEHeaderKey(k)]
|
||||||
|
}
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustGetJoined obtains the values for a given key
|
||||||
|
// with joined values with "," symbol
|
||||||
|
func (md Metadata) MustGetJoined(k string) string {
|
||||||
|
v, ok := md.GetJoined(k)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata key")
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJoined obtains the values for a given key
|
||||||
|
// with joined values with "," symbol
|
||||||
|
func (md Metadata) GetJoined(k string) (string, bool) {
|
||||||
|
v, ok := md.Get(k)
|
||||||
|
if !ok {
|
||||||
|
return "", ok
|
||||||
|
}
|
||||||
|
return strings.Join(v, ","), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of a given key with a slice of values.
|
||||||
|
func (md Metadata) Add(key string, vals ...string) {
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
md[key] = vals
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of a given key with a slice of values.
|
||||||
|
func (md Metadata) Set(kvs ...string) {
|
||||||
|
if len(kvs)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: Set got an odd number of input pairs for metadata: %d", len(kvs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(kvs); i += 2 {
|
||||||
|
md[kvs[i]] = append(md[kvs[i]], kvs[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append adds the values to key k, not overwriting what was already stored at
|
||||||
|
// that key.
|
||||||
|
func (md Metadata) Append(key string, vals ...string) {
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
md[key] = append(md[key], vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del removes the values for a given keys k.
|
||||||
|
func (md Metadata) Del(k ...string) {
|
||||||
|
for i := range k {
|
||||||
|
delete(md, k[i])
|
||||||
|
delete(md, strings.ToLower(k[i]))
|
||||||
|
delete(md, textproto.CanonicalMIMEHeaderKey(k[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join joins any number of Metadatas into a single Metadata.
|
||||||
|
//
|
||||||
|
// The order of values for each key is determined by the order in which the Metadatas
|
||||||
|
// containing those values are presented to Join.
|
||||||
|
func Join(mds ...Metadata) Metadata {
|
||||||
|
out := Metadata{}
|
||||||
|
for _, Metadata := range mds {
|
||||||
|
for k, v := range Metadata {
|
||||||
|
out[k] = append(out[k], v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
metadataIncomingKey struct{}
|
||||||
|
metadataOutgoingKey struct{}
|
||||||
|
metadataCurrentKey struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewContext creates a new context with Metadata attached. Metadata must
|
||||||
|
// not be modified after calling this function.
|
||||||
|
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIncomingContext creates a new context with incoming Metadata attached. Metadata must
|
||||||
|
// not be modified after calling this function.
|
||||||
|
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataIncomingKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOutgoingContext creates a new context with outgoing Metadata attached. If used
|
||||||
|
// in conjunction with AppendOutgoingContext, NewOutgoingContext will
|
||||||
|
// overwrite any previously-appended metadata. Metadata must not be modified after
|
||||||
|
// calling this function.
|
||||||
|
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendContext returns a new context with the provided kv merged
|
||||||
|
// with any existing metadata in the context. Please refer to the documentation
|
||||||
|
// of Pairs for a description of kv.
|
||||||
|
func AppendContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: AppendContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md, _ := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
added := make([][]string, len(md.added)+1)
|
||||||
|
copy(added, md.added)
|
||||||
|
kvCopy := make([]string, 0, len(kv))
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
||||||
|
}
|
||||||
|
added[len(added)-1] = kvCopy
|
||||||
|
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md.md, added: added})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendIncomingContext returns a new context with the provided kv merged
|
||||||
|
// with any existing metadata in the context. Please refer to the documentation
|
||||||
|
// of Pairs for a description of kv.
|
||||||
|
func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: AppendIncomingContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md, _ := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
||||||
|
added := make([][]string, len(md.added)+1)
|
||||||
|
copy(added, md.added)
|
||||||
|
kvCopy := make([]string, 0, len(kv))
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
||||||
|
}
|
||||||
|
added[len(added)-1] = kvCopy
|
||||||
|
return context.WithValue(ctx, metadataIncomingKey{}, rawMetadata{md: md.md, added: added})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendOutgoingContext returns a new context with the provided kv merged
|
||||||
|
// with any existing metadata in the context. Please refer to the documentation
|
||||||
|
// of Pairs for a description of kv.
|
||||||
|
func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: AppendOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md, _ := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
added := make([][]string, len(md.added)+1)
|
||||||
|
copy(added, md.added)
|
||||||
|
kvCopy := make([]string, 0, len(kv))
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
||||||
|
}
|
||||||
|
added[len(added)-1] = kvCopy
|
||||||
|
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md.md, added: added})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext returns the metadata in ctx if it exists.
|
||||||
|
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustContext returns the metadata in ctx.
|
||||||
|
func MustContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIncomingContext returns the incoming metadata in ctx if it exists.
|
||||||
|
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromIncomingContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustIncomingContext returns the incoming metadata in ctx.
|
||||||
|
func MustIncomingContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromIncomingContext returns the metadata value corresponding to the metadata
|
||||||
|
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
||||||
|
// manner.
|
||||||
|
func ValueFromIncomingContext(ctx context.Context, key string) []string {
|
||||||
|
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := raw.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range raw.md {
|
||||||
|
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromCurrentContext returns the metadata value corresponding to the metadata
|
||||||
|
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
||||||
|
// manner.
|
||||||
|
func ValueFromCurrentContext(ctx context.Context, key string) []string {
|
||||||
|
md, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := md.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range md.md {
|
||||||
|
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustOutgoingContext returns the outgoing metadata in ctx.
|
||||||
|
func MustOutgoingContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromOutgoingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromOutgoingContext returns the metadata value corresponding to the metadata
|
||||||
|
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
||||||
|
// manner.
|
||||||
|
func ValueFromOutgoingContext(ctx context.Context, key string) []string {
|
||||||
|
md, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := md.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range md.md {
|
||||||
|
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyOf(v []string) []string {
|
||||||
|
vals := make([]string, len(v))
|
||||||
|
copy(vals, v)
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOutgoingContext returns the outgoing metadata in ctx if it exists.
|
||||||
|
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawMetadata struct {
|
||||||
|
md Metadata
|
||||||
|
added [][]string
|
||||||
|
}
|
||||||
|
|
||||||
// Iterator used to iterate over metadata with order
|
// Iterator used to iterate over metadata with order
|
||||||
type Iterator struct {
|
type Iterator struct {
|
||||||
md Metadata
|
md Metadata
|
||||||
@ -46,6 +464,7 @@ type Iterator struct {
|
|||||||
cnt int
|
cnt int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Next advance iterator to next element
|
// Next advance iterator to next element
|
||||||
func (iter *Iterator) Next(k, v *string) bool {
|
func (iter *Iterator) Next(k, v *string) bool {
|
||||||
if iter.cur+1 > iter.cnt {
|
if iter.cur+1 > iter.cnt {
|
||||||
@ -53,122 +472,19 @@ func (iter *Iterator) Next(k, v *string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*k = iter.keys[iter.cur]
|
*k = iter.keys[iter.cur]
|
||||||
*v = iter.md[*k]
|
*v = iter.Metadata[*k]
|
||||||
iter.cur++
|
iter.cur++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterator returns the itarator for metadata in sorted order
|
// Iterator returns the itarator for metadata in sorted order
|
||||||
func (md Metadata) Iterator() *Iterator {
|
func (Metadata Metadata) Iterator() *Iterator {
|
||||||
iter := &Iterator{md: md, cnt: len(md)}
|
iter := &Iterator{Metadata: Metadata, cnt: len(Metadata)}
|
||||||
iter.keys = make([]string, 0, iter.cnt)
|
iter.keys = make([]string, 0, iter.cnt)
|
||||||
for k := range md {
|
for k := range Metadata {
|
||||||
iter.keys = append(iter.keys, k)
|
iter.keys = append(iter.keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(iter.keys)
|
sort.Strings(iter.keys)
|
||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
func (md Metadata) MustGet(key string) string {
|
|
||||||
val, ok := md.Get(key)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata key")
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of items.
|
|
||||||
func (md Metadata) Len() int {
|
|
||||||
return len(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns value from metadata by key
|
|
||||||
func (md Metadata) Get(key string) (string, bool) {
|
|
||||||
// fast path
|
|
||||||
val, ok := md[key]
|
|
||||||
if !ok {
|
|
||||||
// slow path
|
|
||||||
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
|
|
||||||
if !ok {
|
|
||||||
val, ok = md[strings.ToLower(key)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set is used to store value in metadata
|
|
||||||
func (md Metadata) Set(kv ...string) {
|
|
||||||
if len(kv)%2 == 1 {
|
|
||||||
kv = kv[:len(kv)-1]
|
|
||||||
}
|
|
||||||
for idx := 0; idx < len(kv); idx += 2 {
|
|
||||||
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Del is used to remove value from metadata
|
|
||||||
func (md Metadata) Del(keys ...string) {
|
|
||||||
for _, key := range keys {
|
|
||||||
// fast path
|
|
||||||
delete(md, key)
|
|
||||||
// slow path
|
|
||||||
delete(md, textproto.CanonicalMIMEHeaderKey(key))
|
|
||||||
// very slow path
|
|
||||||
delete(md, strings.ToLower(key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a copy of the metadata
|
|
||||||
func (md Metadata) CopyTo(dst Metadata) {
|
|
||||||
for k, v := range md {
|
|
||||||
dst[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a copy of the metadata
|
|
||||||
func Copy(md Metadata, exclude ...string) Metadata {
|
|
||||||
nmd := New(len(md))
|
|
||||||
for k, v := range md {
|
|
||||||
nmd[k] = v
|
|
||||||
}
|
|
||||||
nmd.Del(exclude...)
|
|
||||||
return nmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// New return new sized metadata
|
|
||||||
func New(size int) Metadata {
|
|
||||||
if size == 0 {
|
|
||||||
size = defaultMetadataSize
|
|
||||||
}
|
|
||||||
return make(Metadata, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges metadata to existing metadata, overwriting if specified
|
|
||||||
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
|
||||||
var ok bool
|
|
||||||
nmd := Copy(omd)
|
|
||||||
for key, val := range mmd {
|
|
||||||
_, ok = nmd[key]
|
|
||||||
switch {
|
|
||||||
case ok && !overwrite:
|
|
||||||
continue
|
|
||||||
case val != "":
|
|
||||||
nmd[key] = val
|
|
||||||
case ok && val == "":
|
|
||||||
nmd.Del(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pairs from which metadata created
|
|
||||||
func Pairs(kv ...string) Metadata {
|
|
||||||
if len(kv)%2 == 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
md := New(len(kv) / 2)
|
|
||||||
for idx := 0; idx < len(kv); idx += 2 {
|
|
||||||
md[kv[idx]] = kv[idx+1]
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
@ -5,10 +5,21 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestAppendOutgoingContextModify(t *testing.T) {
|
||||||
|
md := Pairs("key1", "val1")
|
||||||
|
ctx := NewOutgoingContext(context.TODO(), md)
|
||||||
|
nctx := AppendOutgoingContext(ctx, "key1", "val3", "key2", "val2")
|
||||||
|
_ = nctx
|
||||||
|
omd := MustOutgoingContext(nctx)
|
||||||
|
fmt.Printf("%#+v\n", omd)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func TestLowercase(t *testing.T) {
|
func TestLowercase(t *testing.T) {
|
||||||
md := New(1)
|
md := New(1)
|
||||||
md["x-request-id"] = "12345"
|
md["x-request-id"] = []string{"12345"}
|
||||||
v, ok := md.Get("X-Request-Id")
|
v, ok := md.GetJoined("X-Request-Id")
|
||||||
if !ok || v == "" {
|
if !ok || v == "" {
|
||||||
t.Fatalf("metadata invalid %#+v", md)
|
t.Fatalf("metadata invalid %#+v", md)
|
||||||
}
|
}
|
||||||
@ -38,15 +49,12 @@ func TestMultipleUsage(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataSetMultiple(t *testing.T) {
|
func TestMetadataSetMultiple(t *testing.T) {
|
||||||
md := New(4)
|
md := New(4)
|
||||||
md.Set("key1", "val1", "key2", "val2", "key3")
|
md.Set("key1", "val1", "key2", "val2")
|
||||||
|
|
||||||
if v, ok := md.Get("key1"); !ok || v != "val1" {
|
if v, ok := md.GetJoined("key1"); !ok || v != "val1" {
|
||||||
t.Fatalf("invalid kv %#+v", md)
|
t.Fatalf("invalid kv %#+v", md)
|
||||||
}
|
}
|
||||||
if v, ok := md.Get("key2"); !ok || v != "val2" {
|
if v, ok := md.GetJoined("key2"); !ok || v != "val2" {
|
||||||
t.Fatalf("invalid kv %#+v", md)
|
|
||||||
}
|
|
||||||
if _, ok := md.Get("key3"); ok {
|
|
||||||
t.Fatalf("invalid kv %#+v", md)
|
t.Fatalf("invalid kv %#+v", md)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,22 +72,12 @@ func TestAppend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPairs(t *testing.T) {
|
func TestPairs(t *testing.T) {
|
||||||
md, ok := Pairs("key1", "val1", "key2", "val2")
|
md := Pairs("key1", "val1", "key2", "val2")
|
||||||
if !ok {
|
if _, ok := md.Get("key1"); !ok {
|
||||||
t.Fatal("odd number of kv")
|
|
||||||
}
|
|
||||||
if _, ok = md.Get("key1"); !ok {
|
|
||||||
t.Fatal("key1 not found")
|
t.Fatal("key1 not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCtx(ctx context.Context) {
|
|
||||||
md := New(2)
|
|
||||||
md.Set("Key1", "Val1_new")
|
|
||||||
md.Set("Key3", "Val3")
|
|
||||||
SetOutgoingContext(ctx, md)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPassing(t *testing.T) {
|
func TestPassing(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
md1 := New(2)
|
md1 := New(2)
|
||||||
@ -87,37 +85,24 @@ func TestPassing(t *testing.T) {
|
|||||||
md1.Set("Key2", "Val2")
|
md1.Set("Key2", "Val2")
|
||||||
|
|
||||||
ctx = NewIncomingContext(ctx, md1)
|
ctx = NewIncomingContext(ctx, md1)
|
||||||
testCtx(ctx)
|
|
||||||
_, ok := FromOutgoingContext(ctx)
|
_, ok := FromOutgoingContext(ctx)
|
||||||
if ok {
|
if ok {
|
||||||
t.Fatalf("create outgoing context")
|
t.Fatalf("create outgoing context")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = NewOutgoingContext(ctx, New(1))
|
ctx = NewOutgoingContext(ctx, md1)
|
||||||
testCtx(ctx)
|
|
||||||
md, ok := FromOutgoingContext(ctx)
|
md, ok := FromOutgoingContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("missing metadata from outgoing context")
|
t.Fatalf("missing metadata from outgoing context")
|
||||||
}
|
}
|
||||||
if v, ok := md.Get("Key1"); !ok || v != "Val1_new" {
|
if v, ok := md.Get("Key1"); !ok || v[0] != "Val1" {
|
||||||
t.Fatalf("invalid metadata value %#+v", md)
|
t.Fatalf("invalid metadata value %#+v", md)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
/*
|
||||||
omd := Metadata{
|
|
||||||
"key1": "val1",
|
|
||||||
}
|
|
||||||
mmd := Metadata{
|
|
||||||
"key2": "val2",
|
|
||||||
}
|
|
||||||
|
|
||||||
nmd := Merge(omd, mmd, true)
|
|
||||||
if len(nmd) != 2 {
|
|
||||||
t.Fatalf("merge failed: %v", nmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIterator(_ *testing.T) {
|
func TestIterator(_ *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"1Last": "last",
|
"1Last": "last",
|
||||||
@ -132,24 +117,25 @@ func TestIterator(_ *testing.T) {
|
|||||||
// fmt.Printf("k: %s, v: %s\n", k, v)
|
// fmt.Printf("k: %s, v: %s\n", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func TestMedataCanonicalKey(t *testing.T) {
|
func TestMedataCanonicalKey(t *testing.T) {
|
||||||
md := New(1)
|
md := New(1)
|
||||||
md.Set("x-request-id", "12345")
|
md.Set("x-request-id", "12345")
|
||||||
v, ok := md.Get("x-request-id")
|
v, ok := md.GetJoined("x-request-id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok = md.Get("X-Request-Id")
|
v, ok = md.GetJoined("X-Request-Id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
v, ok = md.Get("X-Request-ID")
|
v, ok = md.GetJoined("X-Request-ID")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if v != "12345" {
|
||||||
@ -162,7 +148,7 @@ func TestMetadataSet(t *testing.T) {
|
|||||||
|
|
||||||
md.Set("Key", "val")
|
md.Set("Key", "val")
|
||||||
|
|
||||||
val, ok := md.Get("Key")
|
val, ok := md.GetJoined("Key")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("key Key not found")
|
t.Fatal("key Key not found")
|
||||||
}
|
}
|
||||||
@ -173,8 +159,8 @@ func TestMetadataSet(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataDelete(t *testing.T) {
|
func TestMetadataDelete(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
"Baz": "empty",
|
"Baz": []string{"empty"},
|
||||||
}
|
}
|
||||||
|
|
||||||
md.Del("Baz")
|
md.Del("Baz")
|
||||||
@ -184,25 +170,16 @@ func TestMetadataDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNilContext(t *testing.T) {
|
|
||||||
var ctx context.Context
|
|
||||||
|
|
||||||
_, ok := FromContext(ctx)
|
|
||||||
if ok {
|
|
||||||
t.Fatal("nil context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetadataCopy(t *testing.T) {
|
func TestMetadataCopy(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
"Bar": "baz",
|
"Bar": []string{"baz"},
|
||||||
}
|
}
|
||||||
|
|
||||||
cp := Copy(md)
|
cp := Copy(md)
|
||||||
|
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
if cv := cp[k]; cv != v {
|
if cv := cp[k]; cv[0] != v[0] {
|
||||||
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +187,7 @@ func TestMetadataCopy(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataContext(t *testing.T) {
|
func TestMetadataContext(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := NewContext(context.TODO(), md)
|
ctx := NewContext(context.TODO(), md)
|
||||||
@ -220,7 +197,7 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if emd["Foo"] != md["Foo"] {
|
if emd["Foo"][0] != md["Foo"][0] {
|
||||||
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,13 +206,88 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopy(t *testing.T) {
|
func TestFromContext(t *testing.T) {
|
||||||
md := New(2)
|
ctx := context.WithValue(context.TODO(), metadataCurrentKey{}, rawMetadata{md: New(0)})
|
||||||
md.Set("key1", "val1", "key2", "val2")
|
|
||||||
nmd := Copy(md, "key2")
|
c, ok := FromContext(ctx)
|
||||||
if len(nmd) != 1 {
|
if c == nil || !ok {
|
||||||
t.Fatal("Copy exclude not works")
|
t.Fatal("FromContext not works")
|
||||||
} else if nmd["Key1"] != "val1" {
|
}
|
||||||
t.Fatal("Copy exclude not works")
|
}
|
||||||
|
|
||||||
|
func TestNewContext(t *testing.T) {
|
||||||
|
ctx := NewContext(context.TODO(), New(0))
|
||||||
|
|
||||||
|
c, ok := FromContext(ctx)
|
||||||
|
if c == nil || !ok {
|
||||||
|
t.Fatal("NewContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIncomingContext(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.TODO(), metadataIncomingKey{}, rawMetadata{md: New(0)})
|
||||||
|
|
||||||
|
c, ok := FromIncomingContext(ctx)
|
||||||
|
if c == nil || !ok {
|
||||||
|
t.Fatal("FromIncomingContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromOutgoingContext(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.TODO(), metadataOutgoingKey{}, rawMetadata{md: New(0)})
|
||||||
|
|
||||||
|
c, ok := FromOutgoingContext(ctx)
|
||||||
|
if c == nil || !ok {
|
||||||
|
t.Fatal("FromOutgoingContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewIncomingContext(t *testing.T) {
|
||||||
|
md := New(1)
|
||||||
|
md.Set("key", "val")
|
||||||
|
ctx := NewIncomingContext(context.TODO(), md)
|
||||||
|
|
||||||
|
c, ok := FromIncomingContext(ctx)
|
||||||
|
if c == nil || !ok {
|
||||||
|
t.Fatal("NewIncomingContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOutgoingContext(t *testing.T) {
|
||||||
|
md := New(1)
|
||||||
|
md.Set("key", "val")
|
||||||
|
ctx := NewOutgoingContext(context.TODO(), md)
|
||||||
|
|
||||||
|
c, ok := FromOutgoingContext(ctx)
|
||||||
|
if c == nil || !ok {
|
||||||
|
t.Fatal("NewOutgoingContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendIncomingContext(t *testing.T) {
|
||||||
|
md := New(1)
|
||||||
|
md.Set("key1", "val1")
|
||||||
|
ctx := AppendIncomingContext(context.TODO(), "key2", "val2")
|
||||||
|
|
||||||
|
nmd, ok := FromIncomingContext(ctx)
|
||||||
|
if nmd == nil || !ok {
|
||||||
|
t.Fatal("AppendIncomingContext not works")
|
||||||
|
}
|
||||||
|
if v, ok := nmd.GetJoined("key2"); !ok || v != "val2" {
|
||||||
|
t.Fatal("AppendIncomingContext not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendOutgoingContext(t *testing.T) {
|
||||||
|
md := New(1)
|
||||||
|
md.Set("key1", "val1")
|
||||||
|
ctx := AppendOutgoingContext(context.TODO(), "key2", "val2")
|
||||||
|
|
||||||
|
nmd, ok := FromOutgoingContext(ctx)
|
||||||
|
if nmd == nil || !ok {
|
||||||
|
t.Fatal("AppendOutgoingContext not works")
|
||||||
|
}
|
||||||
|
if v, ok := nmd.GetJoined("key2"); !ok || v != "val2" {
|
||||||
|
t.Fatal("AppendOutgoingContext not works")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"go.unistack.org/micro/v4/broker"
|
"go.unistack.org/micro/v4/broker"
|
||||||
"go.unistack.org/micro/v4/fsm"
|
"go.unistack.org/micro/v4/fsm"
|
||||||
|
"go.unistack.org/micro/v4/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAs(t *testing.T) {
|
func TestAs(t *testing.T) {
|
||||||
@ -61,6 +62,8 @@ func TestAs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ broker.Broker = (*bro)(nil)
|
||||||
|
|
||||||
type bro struct {
|
type bro struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
@ -87,23 +90,18 @@ func (p *bro) Connect(_ context.Context) error { return nil }
|
|||||||
// Disconnect disconnect from broker
|
// Disconnect disconnect from broker
|
||||||
func (p *bro) Disconnect(_ context.Context) error { return nil }
|
func (p *bro) Disconnect(_ context.Context) error { return nil }
|
||||||
|
|
||||||
// Publish message, msg can be single broker.Message or []broker.Message
|
// NewMessage creates new message
|
||||||
func (p *bro) Publish(_ context.Context, _ string, _ *broker.Message, _ ...broker.PublishOption) error {
|
func (p *bro) NewMessage(_ context.Context, _ metadata.Metadata, _ interface{}, _ ...broker.PublishOption) (broker.Message, error) {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchPublish messages to broker with multiple topics
|
|
||||||
func (p *bro) BatchPublish(_ context.Context, _ []*broker.Message, _ ...broker.PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchSubscribe subscribes to topic messages via handler
|
|
||||||
func (p *bro) BatchSubscribe(_ context.Context, _ string, _ broker.BatchHandler, _ ...broker.SubscribeOption) (broker.Subscriber, error) {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Publish message, msg can be single broker.Message or []broker.Message
|
||||||
|
func (p *bro) Publish(_ context.Context, _ string, _ ...broker.Message) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe subscribes to topic message via handler
|
// Subscribe subscribes to topic message via handler
|
||||||
func (p *bro) Subscribe(_ context.Context, _ string, _ broker.Handler, _ ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (p *bro) Subscribe(_ context.Context, _ string, _ interface{}, _ ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v4/register"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v4/util/id"
|
"go.unistack.org/micro/v3/register"
|
||||||
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -23,10 +24,9 @@ type node struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type record struct {
|
type record struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Metadata map[string]string
|
Nodes map[string]*node
|
||||||
Nodes map[string]*node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type memory struct {
|
type memory struct {
|
||||||
@ -160,19 +160,14 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := make(map[string]string, len(n.Metadata))
|
md := metadata.Copy(n.Metadata)
|
||||||
|
|
||||||
// make copy of metadata
|
|
||||||
for k, v := range n.Metadata {
|
|
||||||
metadata[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the node
|
// add the node
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
||||||
Node: ®ister.Node{
|
Node: ®ister.Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: metadata,
|
Metadata: md,
|
||||||
},
|
},
|
||||||
TTL: options.TTL,
|
TTL: options.TTL,
|
||||||
LastSeen: time.Now(),
|
LastSeen: time.Now(),
|
||||||
@ -452,23 +447,15 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func recordToService(r *record, namespace string) *register.Service {
|
func recordToService(r *record, namespace string) *register.Service {
|
||||||
metadata := make(map[string]string, len(r.Metadata))
|
|
||||||
for k, v := range r.Metadata {
|
|
||||||
metadata[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := make([]*register.Node, len(r.Nodes))
|
nodes := make([]*register.Node, len(r.Nodes))
|
||||||
i := 0
|
i := 0
|
||||||
for _, n := range r.Nodes {
|
for _, n := range r.Nodes {
|
||||||
md := make(map[string]string, len(n.Metadata))
|
nmd := metadata.Copy(n.Metadata)
|
||||||
for k, v := range n.Metadata {
|
|
||||||
md[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = ®ister.Node{
|
nodes[i] = ®ister.Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: md,
|
Metadata: nmd,
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/register"
|
"go.unistack.org/micro/v3/register"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testData = map[string][]*register.Service{
|
var testData = map[string][]*register.Service{
|
||||||
|
@ -77,10 +77,6 @@ func NewRegisterService(s Server) (*register.Service, error) {
|
|||||||
}
|
}
|
||||||
node.Metadata = metadata.Copy(opts.Metadata)
|
node.Metadata = metadata.Copy(opts.Metadata)
|
||||||
|
|
||||||
node.Metadata["server"] = s.String()
|
|
||||||
node.Metadata["broker"] = opts.Broker.String()
|
|
||||||
node.Metadata["register"] = opts.Register.String()
|
|
||||||
|
|
||||||
return ®ister.Service{
|
return ®ister.Service{
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
Version: opts.Version,
|
Version: opts.Version,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user