M bind.go => bind.go +3 -2
@@ 10,6 10,7 @@ import (
"fmt"
"io"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/internal"
"mellium.im/xmpp/internal/ns"
"mellium.im/xmpp/jid"
@@ 27,7 28,7 @@ func BindResource() StreamFeature {
Name: xml.Name{Space: ns.Bind, Local: "bind"},
Necessary: Authn,
Prohibited: Ready,
- List: func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+ List: func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
req = true
if err = e.EncodeToken(start); err != nil {
return req, err
@@ 39,7 40,7 @@ func BindResource() StreamFeature {
err = e.Flush()
return req, err
},
- Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+ Parse: func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
parsed := struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
}{}
A codec/codec.go => codec/codec.go +34 -0
@@ 0,0 1,34 @@
+// Copyright 2017 Sam Whited.
+// Use of this source code is governed by the BSD 2-clause license that can be
+// found in the LICENSE file.
+
+package codec
+
+import (
+ "encoding/xml"
+)
+
+// A Codec is both a Decoder and an Encoder and can handle both sides of an XMPP
+// stream.
+type Codec interface {
+ Decoder
+ Encoder
+}
+
+// A Decoder is anything that can be used to decode an XML token stream
+// (including an *xml.Decoder).
+type Decoder interface {
+ DecodeElement(v interface{}, start *xml.StartElement) error
+ Decode(v interface{}) error
+ Skip() error
+ Token() (xml.Token, error)
+}
+
+// An Encoder is anything that can be used to encode an XML token stream
+// (including an *xml.Encoder)
+type Encoder interface {
+ EncodeElement(v interface{}, start xml.StartElement) error
+ EncodeToken(t xml.Token) error
+ Encode(v interface{}) error
+ Flush() error
+}
A codec/doc.go => codec/doc.go +10 -0
@@ 0,0 1,10 @@
+// Copyright 2017 Sam Whited.
+// Use of this source code is governed by the BSD 2-clause license that can be
+// found in the LICENSE file.
+
+// Package codec provides functionality for serializing and deserializing an
+// XMPP stream from its native XML encoding and for creating new serialization
+// formats.
+//
+// Be advised: This API is still unstable and is subject to change.
+package codec // import "mellium.im/xmpp/codec"
M compress/compression.go => compress/compression.go +3 -2
@@ 14,6 14,7 @@ import (
"io"
"mellium.im/xmpp"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/stream"
)
@@ 37,7 38,7 @@ func New(methods ...Method) xmpp.StreamFeature {
return xmpp.StreamFeature{
Name: xml.Name{Local: "compression", Space: NSFeatures},
Necessary: xmpp.Secure | xmpp.Authn,
- List: func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+ List: func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
if err = e.EncodeToken(start); err != nil {
return
}
@@ 67,7 68,7 @@ func New(methods ...Method) xmpp.StreamFeature {
}
return false, e.Flush()
},
- Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+ Parse: func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
listed := struct {
XMLName xml.Name `xml:"http://jabber.org/features/compress compression"`
Methods []string `xml:"http://jabber.org/features/compress method"`
M features.go => features.go +3 -2
@@ 9,6 9,7 @@ import (
"encoding/xml"
"io"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/internal/ns"
"mellium.im/xmpp/stream"
)
@@ 39,14 40,14 @@ type StreamFeature struct {
// used as the outermost tag in the stream (but also may be ignored). List
// implementations that call e.EncodeToken directly need to call e.Flush when
// finished to ensure that the XML is written to the underlying writer.
- List func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error)
+ List func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error)
// Used to parse the feature that begins with the given xml start element
// (which should have a Name that matches this stream feature's Name).
// Returns whether or not the feature is required, and any data that will be
// needed if the feature is selected for negotiation (eg. the list of
// mechanisms if the feature was SASL).
- Parse func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (req bool, data interface{}, err error)
+ Parse func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (req bool, data interface{}, err error)
// A function that will take over the session temporarily while negotiating
// the feature. The "mask" SessionState represents the state bits that should
M ibr2/challenge.go => ibr2/challenge.go +5 -3
@@ 7,6 7,8 @@ package ibr2
import (
"context"
"encoding/xml"
+
+ "mellium.im/xmpp/codec"
)
// Challenge is an IBR challenge.
@@ 16,12 18,12 @@ type Challenge struct {
Type string
// Send is used by the server to send the challenge to the client.
- Send func(ctx context.Context, e *xml.Encoder) error
+ Send func(ctx context.Context, e codec.Encoder) error
// Respond is used by the client to send a response or reply to the challenge.
- Respond func(context.Context, *xml.Encoder) error
+ Respond func(context.Context, codec.Encoder) error
// Receive is used by the client to receive and decode the server's challenge
// and by the server to receive and decode the clients response.
- Receive func(ctx context.Context, server bool, d *xml.Decoder, start *xml.StartElement) error
+ Receive func(ctx context.Context, server bool, d codec.Decoder, start *xml.StartElement) error
}
M ibr2/ibr2.go => ibr2/ibr2.go +6 -5
@@ 16,6 16,7 @@ import (
"io"
"mellium.im/xmpp"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/stream"
)
@@ 43,8 44,8 @@ func challengeStart(typ string) xml.StartElement {
}
}
-func listFunc(challenges ...Challenge) func(context.Context, *xml.Encoder, xml.StartElement) (bool, error) {
- return func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+func listFunc(challenges ...Challenge) func(context.Context, codec.Encoder, xml.StartElement) (bool, error) {
+ return func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
if err = e.EncodeToken(start); err != nil {
return
}
@@ 77,8 78,8 @@ func listFunc(challenges ...Challenge) func(context.Context, *xml.Encoder, xml.S
}
}
-func parseFunc(challenges ...Challenge) func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (req bool, supported interface{}, err error) {
- return func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+func parseFunc(challenges ...Challenge) func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (req bool, supported interface{}, err error) {
+ return func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
// Parse the list of challenge types sent down by the server.
parsed := struct {
Challenges []string `xml:"urn:xmpp:register:0 challenge"`
@@ 105,7 106,7 @@ func parseFunc(challenges ...Challenge) func(ctx context.Context, d *xml.Decoder
}
}
-func decodeClientResp(ctx context.Context, d *xml.Decoder, decode func(ctx context.Context, server bool, d *xml.Decoder, start *xml.StartElement) error) (cancel bool, err error) {
+func decodeClientResp(ctx context.Context, d codec.Decoder, decode func(ctx context.Context, server bool, d codec.Decoder, start *xml.StartElement) error) (cancel bool, err error) {
var tok xml.Token
tok, err = d.Token()
if err != nil {
M ibr2/oob.go => ibr2/oob.go +3 -2
@@ 8,6 8,7 @@ import (
"context"
"encoding/xml"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/oob"
)
@@ 19,10 20,10 @@ import (
func OOB(data *oob.Data, f func(*oob.Data) error) Challenge {
return Challenge{
Type: oob.NS,
- Send: func(ctx context.Context, e *xml.Encoder) error {
+ Send: func(ctx context.Context, e codec.Encoder) error {
return e.Encode(data)
},
- Receive: func(ctx context.Context, server bool, d *xml.Decoder, start *xml.StartElement) error {
+ Receive: func(ctx context.Context, server bool, d codec.Decoder, start *xml.StartElement) error {
// The server does not receive a reply for this mechanism.
if server {
return nil
M sasl.go => sasl.go +4 -3
@@ 13,6 13,7 @@ import (
"io"
"mellium.im/sasl"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/internal/ns"
"mellium.im/xmpp/internal/saslerr"
"mellium.im/xmpp/stream"
@@ 32,7 33,7 @@ func SASL(mechanisms ...sasl.Mechanism) StreamFeature {
Name: xml.Name{Space: ns.SASL, Local: "mechanisms"},
Necessary: Secure,
Prohibited: Authn,
- List: func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+ List: func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
req = true
if err = e.EncodeToken(start); err != nil {
return
@@ 58,7 59,7 @@ func SASL(mechanisms ...sasl.Mechanism) StreamFeature {
}
return req, e.EncodeToken(start.End())
},
- Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+ Parse: func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
parsed := struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
List []string `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism"`
@@ 181,7 182,7 @@ func SASL(mechanisms ...sasl.Mechanism) StreamFeature {
}
}
-func decodeSASLChallenge(d *xml.Decoder, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
+func decodeSASLChallenge(d codec.Decoder, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
switch start.Name {
case xml.Name{Space: ns.SASL, Local: "challenge"}, xml.Name{Space: ns.SASL, Local: "success"}:
if !allowChallenge && start.Name.Local == "challenge" {
M sasl2/sasl.go => sasl2/sasl.go +4 -3
@@ 20,6 20,7 @@ import (
"mellium.im/sasl"
"mellium.im/xmpp"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/internal/saslerr"
"mellium.im/xmpp/stream"
)
@@ 48,7 49,7 @@ func SASL(mechanisms ...sasl.Mechanism) xmpp.StreamFeature {
Name: xml.Name{Space: NS, Local: "mechanisms"},
Necessary: xmpp.Secure,
Prohibited: xmpp.Authn,
- List: func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+ List: func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
req = true
if err = e.EncodeToken(start); err != nil {
return
@@ 74,7 75,7 @@ func SASL(mechanisms ...sasl.Mechanism) xmpp.StreamFeature {
}
return req, e.EncodeToken(start.End())
},
- Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+ Parse: func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
parsed := struct {
XMLName xml.Name `xml:"urn:xmpp:sasl:0 mechanisms"`
List []string `xml:"urn:xmpp:sasl:0 mechanism"`
@@ 201,7 202,7 @@ func SASL(mechanisms ...sasl.Mechanism) xmpp.StreamFeature {
}
}
-func decodeSASLChallenge(d *xml.Decoder, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
+func decodeSASLChallenge(d codec.Decoder, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
switch start.Name {
case xml.Name{Space: NS, Local: "challenge"}:
if !allowChallenge {
M session.go => session.go +6 -5
@@ 12,6 12,7 @@ import (
"net"
"sync"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/stream"
)
@@ 75,7 76,7 @@ type Session struct {
in struct {
sync.Mutex
streamInfo
- d *xml.Decoder
+ d codec.Decoder
ctx context.Context
cancel context.CancelFunc
}
@@ 135,13 136,13 @@ func (s *Session) Conn() io.ReadWriter {
return s.rw
}
-// Decoder returns the XML decoder that was used to negotiate the latest stream.
-func (s *Session) Decoder() *xml.Decoder {
+// Decoder returns the decoder that was used to negotiate the latest stream.
+func (s *Session) Decoder() codec.Decoder {
return s.in.d
}
-// Encoder returns the XML encoder that was used to negotiate the latest stream.
-func (s *Session) Encoder() *xml.Encoder {
+// Encoder returns the encoder that was used to negotiate the latest stream.
+func (s *Session) Encoder() codec.Encoder {
return s.out.e
}
M starttls.go => starttls.go +3 -2
@@ 13,6 13,7 @@ import (
"io"
"net"
+ "mellium.im/xmpp/codec"
"mellium.im/xmpp/internal/ns"
"mellium.im/xmpp/stream"
)
@@ 31,7 32,7 @@ func StartTLS(required bool) StreamFeature {
return StreamFeature{
Name: xml.Name{Local: "starttls", Space: ns.StartTLS},
Prohibited: Secure,
- List: func(ctx context.Context, e *xml.Encoder, start xml.StartElement) (req bool, err error) {
+ List: func(ctx context.Context, e codec.Encoder, start xml.StartElement) (req bool, err error) {
if err = e.EncodeToken(start); err != nil {
return required, err
}
@@ 46,7 47,7 @@ func StartTLS(required bool) StreamFeature {
}
return required, e.EncodeToken(start.End())
},
- Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
+ Parse: func(ctx context.Context, d codec.Decoder, start *xml.StartElement) (bool, interface{}, error) {
parsed := struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
Required struct {