From 836e7232731db0db283f64907625038abe56deeb Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 3 Jun 2019 12:40:42 +0200 Subject: [PATCH] Refactor / extract the registry Work in progress --- iq.go | 16 ++++-------- message.go | 47 ++++++----------------------------- msg_receipts.go | 23 +++++++++++++++++ registry.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 msg_receipts.go create mode 100644 registry.go diff --git a/iq.go b/iq.go index ec26114..00ec811 100644 --- a/iq.go +++ b/iq.go @@ -210,7 +210,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { if level <= 1 { var elt interface{} payloadType := tt.Name.Space + " " + tt.Name.Local - if payloadType := typeRegistry[payloadType]; payloadType != nil { + if payloadType := iqTypeRegistry[payloadType]; payloadType != nil { val := reflect.New(payloadType) elt = val.Interface() } else { @@ -332,15 +332,9 @@ type DiscoItem struct { Node string `xml:"node,attr,omitempty"` } -// ============================================================================ -// TODO: Make it configurable at to be able to easily add new XMPP extensions -// in separate modules - -var typeRegistry = make(map[string]reflect.Type) - func init() { - typeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{}) - typeRegistry["http://jabber.org/protocol/disco#items query"] = reflect.TypeOf(DiscoItems{}) - typeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{}) - typeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{}) + iqTypeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{}) + iqTypeRegistry["http://jabber.org/protocol/disco#items query"] = reflect.TypeOf(DiscoItems{}) + iqTypeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{}) + iqTypeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{}) } diff --git a/message.go b/message.go index af815e1..039df80 100644 --- a/message.go +++ b/message.go @@ -3,7 +3,6 @@ package xmpp // import "gosrc.io/xmpp" import ( "encoding/xml" "fmt" - "reflect" ) // ============================================================================ @@ -87,21 +86,16 @@ func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt := t.(type) { case xml.StartElement: - var elt interface{} elementType := tt.Name.Space - - if extensionType := msgTypeRegistry[elementType]; extensionType != nil { - val := reflect.New(extensionType) - elt = val.Interface() - if msgExt, ok := elt.(MsgExtension); ok { - err = d.DecodeElement(elt, &tt) - if err != nil { - return err - } - msg.Extensions = append(msg.Extensions, msgExt) + if msgExt := typeRegistry.getmsgType(elementType); msgExt != nil { + // Decode message extension + err = d.DecodeElement(msgExt, &tt) + if err != nil { + return err } + msg.Extensions = append(msg.Extensions, msgExt) } else { - // Decode default message elements + // Decode standard message sub-elements var err error switch tt.Name.Local { case "body": @@ -125,30 +119,3 @@ func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } } } - -// ============================================================================ -// Message extensions -// Provide ability to add support to XMPP extension tags on messages - -type MsgExtension interface { - IsMsgExtension() -} - -// XEP-0184 -type Receipt struct { - // xmlns: urn:xmpp:receipts - XMLName xml.Name - Id string -} - -func (*Receipt) IsMsgExtension() {} - -// ============================================================================ -// TODO: Make it configurable at to be able to easily add new XMPP extensions -// in separate modules - -var msgTypeRegistry = make(map[string]reflect.Type) - -func init() { - msgTypeRegistry["urn:xmpp:receipts"] = reflect.TypeOf(Receipt{}) -} diff --git a/msg_receipts.go b/msg_receipts.go new file mode 100644 index 0000000..5d317b3 --- /dev/null +++ b/msg_receipts.go @@ -0,0 +1,23 @@ +package xmpp + +import "encoding/xml" + +/* +Support for: +- XEP-0184 - Message Delivery Receipts: https://xmpp.org/extensions/xep-0184.html +*/ + +const ( + NSReceipts = "urn:xmpp:receipts" +) + +// XEP-0184 message receipt markers +type Receipt struct { + MsgExtension + XMLName xml.Name + Id string +} + +func init() { + typeRegistry.RegisterMsgExt(NSReceipts, Receipt{}) +} diff --git a/registry.go b/registry.go new file mode 100644 index 0000000..6f62199 --- /dev/null +++ b/registry.go @@ -0,0 +1,65 @@ +package xmpp + +import ( + "reflect" + "sync" +) + +type MsgExtension interface{} + +// The Registry for msg and IQ types is a global variable. +// TODO: Move to the client init process to remove the dependency on a global variable. +// That should make it possible to be able to share the decoder. +// TODO: Ensure that a client can add its own custom namespace to the registry (or overload existing ones). +var typeRegistry = newRegistry() + +type namespace = string + +type registry struct { + // Key is namespace of message extension + msgTypes map[namespace]reflect.Type + msgTypesLock *sync.RWMutex + + iqTypes map[namespace]reflect.Type +} + +func newRegistry() registry { + return registry{ + msgTypes: make(map[namespace]reflect.Type), + msgTypesLock: &sync.RWMutex{}, + iqTypes: make(map[namespace]reflect.Type), + } +} + +// Mutexes are not needed when adding a Message or IQ extension in init function. +// However, forcing the use of the mutex protect the data structure against unexpected use +// of the registry by developers using the library. +func (r registry) RegisterMsgExt(namespace string, extension MsgExtension) { + r.msgTypesLock.Lock() + defer r.msgTypesLock.Unlock() + r.msgTypes[namespace] = reflect.TypeOf(extension) +} + +func (r registry) getMsgExtType(namespace string) reflect.Type { + r.msgTypesLock.RLock() + defer r.msgTypesLock.RUnlock() + return r.msgTypes[namespace] +} + +func (r registry) getmsgType(namespace string) MsgExtension { + if extensionType := r.getMsgExtType(namespace); extensionType != nil { + val := reflect.New(extensionType) + elt := val.Interface() + if msgExt, ok := elt.(MsgExtension); ok { + return msgExt + } + } + return nil +} + +// Registry to support message extensions +//var msgTypeRegistry = make(map[string]reflect.Type) + +// Registry to instantiate the right IQ payload element +// Key is namespace and key of the payload +var iqTypeRegistry = make(map[string]reflect.Type)