Implement error parsing

This commit is contained in:
Mickael Remond 2018-01-20 18:09:13 +01:00
parent bbfafbb32c
commit 8470c01c09
No known key found for this signature in database
GPG key ID: E6F6045D79965AA3
2 changed files with 120 additions and 4 deletions

98
iq.go
View file

@ -6,6 +6,8 @@ import (
"reflect"
"strconv"
"fluux.io/xmpp/iot"
)
@ -60,6 +62,92 @@ TODO support ability to put Raw payload
*/
// ============================================================================
// XMPP Errors
type Err struct {
XMLName xml.Name `xml:"error"`
Reason string
Code int `xml:"code,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Text string `xml:"urn:ietf:params:xml:ns:xmpp-stanzas text"`
}
// UnmarshalXML implements custom parsing for IQs
func (x *Err) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
x.XMLName = start.Name
// Extract attributes
for _, attr := range start.Attr {
if attr.Name.Local == "type" {
x.Type = attr.Value
}
if attr.Name.Local == "code" {
if code, err := strconv.Atoi(attr.Value); err == nil {
x.Code = code
}
}
}
for {
t, err := d.Token()
if err != nil {
return err
}
switch tt := t.(type) {
case xml.StartElement:
elt := new(Node)
err = d.DecodeElement(elt, &tt)
if err != nil {
return err
}
textName := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: "text"}
if elt.XMLName == textName {
x.Text = string(elt.Content)
} else if elt.XMLName.Space == "urn:ietf:params:xml:ns:xmpp-stanzas" {
x.Reason = elt.XMLName.Local
}
case xml.EndElement:
if tt == start.End() {
return nil
}
}
}
}
func (x Err) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
code := xml.Attr{
Name: xml.Name{Local: "code"},
Value: strconv.Itoa(x.Code),
}
typ := xml.Attr{
Name: xml.Name{Local: "type"},
Value: x.Type,
}
start.Name = xml.Name{Local: "error"}
start.Attr = append(start.Attr, code, typ)
err = e.EncodeToken(start)
// Subtags
// Reason
reason := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: x.Reason}
e.EncodeToken(xml.StartElement{Name: reason})
e.EncodeToken(xml.EndElement{Name: reason})
// Text
text := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: "text"}
e.EncodeToken(xml.StartElement{Name: text})
e.EncodeToken(xml.CharData(x.Text))
e.EncodeToken(xml.EndElement{Name: text})
return e.EncodeToken(xml.EndElement{Name: start.Name})
}
// ============================================================================
// IQ Packet
@ -68,7 +156,7 @@ type IQ struct { // Info/Query
PacketAttrs
Payload []IQPayload `xml:",omitempty"`
RawXML string `xml:",innerxml"`
// Error clientError
Error Err `xml:"error,omitempty"`
}
func NewIQ(iqtype, from, to, id, lang string) IQ {
@ -196,7 +284,7 @@ type IQPayload interface {
type Node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:"-"`
// Content []byte `xml:",innerxml"`
Content string `xml:",innerxml"`
Nodes []Node `xml:",any"`
}
@ -217,7 +305,7 @@ func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return d.DecodeElement((*node)(n), &start)
}
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
func (n Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
start.Attr = n.Attrs
start.Name = n.XMLName
@ -231,6 +319,10 @@ func (*Node) IsIQPayload() {}
// ============================================================================
// Disco
const (
NSDiscoInfo = "http://jabber.org/protocol/disco#info"
)
type DiscoInfo struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
Identity Identity `xml:"identity"`

View file

@ -61,6 +61,30 @@ func TestGenerateIq(t *testing.T) {
}
}
func TestErrorTag(t *testing.T) {
xError := Err{
XMLName: xml.Name{Local: "error"},
Code: 503,
Type: "cancel",
Reason: "service-unavailable",
Text: "User session not found",
}
data, err := xml.Marshal(xError)
if err != nil {
t.Errorf("cannot marshal xml structure: %s", err)
}
parsedError := Err{}
if err = xml.Unmarshal(data, &parsedError); err != nil {
t.Errorf("Unmarshal(%s) returned error", data)
}
if !xmlEqual(parsedError, xError) {
t.Errorf("non matching items\n%s", cmp.Diff(parsedError, xError))
}
}
// Compare iq structure but ignore empty namespace as they are set properly on
// marshal / unmarshal. There is no need to manage them on the manually
// crafted structure.