~samwhited/xmpp

eee107ad904bfa3fb41bc26c826ddb926ee26be5 — Sam Whited 10 months ago 2e83177 session_decoder
all: change handler to provide a decoder

This patch changes the definition of handlers from taking an
xmlstream.TokenReadEncoder to the new xmlstream.TokenDecodeEncoder.
This is a breaking API change.

I've changed the definition of Handlers several times in this past, and
was loath to change it again becaues it's a major breaking change that
will affect almost all users of this module, but I decided it was
necessary once again.

While working on new packages, I was writing something like the
following as the first line of every handler:

    d := xml.NewTokenDecoder(tokenReader)

I had also introduced bugs that broke this several times (eg. because
the first start token had already been consumed and the new Decoder
would encounter the end token and break). This led me to think that we
needed to provide a decoder internally so that we could already use it
to pop tokens and make sure its internal state tracking was up to date
with the underlying stream, and to reduce the amount of boilerplate code
users of the Handler types have to write.

A simpler argument for the change might just be one of consistency: we
were previously returning an Encoder for writing tokens, but only a
TokenReader for reading them. Why would we provide more functionality
for writing tokens than we would for reading them? The answer is likely
that we can construct a Decoder from the TokenReader but we can't
construct an Encoder from a TokenWriter with the current functionality
of encoding/xml, but it's still nice for us to just provide a symetric
API to the users.

This feels like the logical next evolution of the API. I am not sure why
it wasn't done the previous time when we changed to an
xmlstream.TokenReadEncoder, but it is done now and I don't see anywhere
else we could really go with this particular parameter so hopefully
things will be more stable as we continue to progress towards 1.0.

Signed-off-by: Sam Whited <sam@samwhited.com>
M bind.go => bind.go +5 -6
@@ 100,10 100,9 @@ func bind(server func(jid.JID, string) (jid.JID, error)) StreamFeature {
			return true, nil, xml.NewTokenDecoder(r).DecodeElement(&parsed, start)
		},
		Negotiate: func(ctx context.Context, session *Session, data interface{}) (mask SessionState, rw io.ReadWriter, err error) {
			r := session.TokenReader()
			defer r.Close()
			d := xml.NewTokenDecoder(r)
			w := session.TokenWriter()
			d := session.Decoder()
			defer d.Close()
			w := session.Encoder()
			defer w.Close()

			// Handle the server side of resource binding if we're on the receiving


@@ 161,7 160,7 @@ func bind(server func(jid.JID, string) (jid.JID, error)) StreamFeature {
				if err != nil {
					return mask, nil, err
				}
				return mask, nil, w.Flush()
				return mask, nil, w.(xmlstream.Flusher).Flush()
			}

			// Client encodes an IQ requesting resource binding.


@@ 179,7 178,7 @@ func bind(server func(jid.JID, string) (jid.JID, error)) StreamFeature {
			if err != nil {
				return mask, nil, err
			}
			if err = w.Flush(); err != nil {
			if err = w.(xmlstream.Flusher).Flush(); err != nil {
				return mask, nil, err
			}


M compress/compression.go => compress/compression.go +3 -4
@@ 86,9 86,8 @@ func New(methods ...Method) xmpp.StreamFeature {
		},
		Negotiate: func(ctx context.Context, session *xmpp.Session, data interface{}) (mask xmpp.SessionState, rw io.ReadWriter, err error) {
			conn := session.Conn()
			r := session.TokenReader()
			defer r.Close()
			d := xml.NewTokenDecoder(r)
			d := session.Decoder()
			defer d.Close()

			// If we're a server.
			if (session.State() & xmpp.Received) == xmpp.Received {


@@ 154,7 153,7 @@ func New(methods ...Method) xmpp.StreamFeature {
			}

			if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "compressed" && t.Name.Space == NSProtocol {
				if err = d.Skip(); err != nil {
				if err = xmlstream.Skip(d); err != nil {
					return mask, nil, err
				}
				rw, err = selected.Wrapper(conn)

M doc.go => doc.go +5 -12
@@ 73,14 73,9 @@
// required or may be received out of order.
// This is accomplished with two XML streams: an input stream and an output
// stream.
// To receive XML on the input stream, Session implements the xml.TokenReader
// interface defined in encoding/xml; this allows session to be wrapped with
// xml.NewTokenDecoder.
// To send XML on the output stream, Session has a TokenEncoder method that
// returns a token encoder that holds a lock on the output stream until it is
// closed.
// The session may also buffer writes and has a Flush method which will write
// any buffered XML to the underlying connection.
// To receive and send XML, Session has the Decoder and Encoder methods that
// hold a lock on the input or output streams until the returned closers are
// called.
//
// Writing individual XML tokens can be tedious and error prone.
// The stanza package contains functions and structs that aid in the


@@ 102,9 97,7 @@
// stream to the end of the element when the handler returns (even if the
// handler did not consume the entire element).
//
//     err := session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
//         d := xml.NewTokenDecoder(t)
//
//     err := session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenDecodeEncoder, start *xml.StartElement) error {
//         // Ignore anything that's not a message.
//         if start.Name.Local != "message" {
//             return nil


@@ 114,7 107,7 @@
//             stanza.Message
//             Body string `xml:"body"`
//         }{}
//         err := d.DecodeElement(&msg, start)
//         err := t.DecodeElement(&msg, start)
//         …
//         if msg.Body != "" {
//             log.Println("Got message: %q", msg.Body)

M echobot_example_test.go => echobot_example_test.go +2 -4
@@ 62,9 62,7 @@ func Example_echobot() {
		return
	}

	s.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		d := xml.NewTokenDecoder(t)

	s.Serve(xmpp.HandlerFunc(func(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
		// Ignore anything that's not a message. In a real system we'd want to at
		// least respond to IQs.
		if start.Name.Local != "message" {


@@ 72,7 70,7 @@ func Example_echobot() {
		}

		msg := MessageBody{}
		err = d.DecodeElement(&msg, start)
		err = t.DecodeElement(&msg, start)
		if err != nil && err != io.EOF {
			log.Printf("Error decoding message: %q", err)
			return nil

M examples/echobot/echo.go => examples/echobot/echo.go +2 -4
@@ 76,9 76,7 @@ func echo(ctx context.Context, addr, pass string, xmlIn, xmlOut io.Writer, logge
		return fmt.Errorf("Error sending initial presence: %w", err)
	}

	return s.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		d := xml.NewTokenDecoder(t)

	return s.Serve(xmpp.HandlerFunc(func(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
		// Ignore anything that's not a message. In a real system we'd want to at
		// least respond to IQs.
		if start.Name.Local != "message" {


@@ 86,7 84,7 @@ func echo(ctx context.Context, addr, pass string, xmlIn, xmlOut io.Writer, logge
		}

		msg := MessageBody{}
		err = d.DecodeElement(&msg, start)
		err = t.DecodeElement(&msg, start)
		if err != nil && err != io.EOF {
			logger.Printf("Error decoding message: %q", err)
			return nil

M examples/msgrepl/main.go => examples/msgrepl/main.go +2 -3
@@ 94,8 94,7 @@ func main() {

	// Handle incoming messages.
	go func() {
		err := session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			d := xml.NewTokenDecoder(t)
		err := session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenDecodeEncoder, start *xml.StartElement) error {
			// Ignore anything that's not a message. In a real system we'd want to at
			// least respond to IQs.
			if start.Name.Local != "message" {


@@ 103,7 102,7 @@ func main() {
			}

			msg := messageBody{}
			err = d.DecodeElement(&msg, start)
			err = t.DecodeElement(&msg, start)
			if err != nil && err != io.EOF {
				logger.Printf("Error decoding message: %q", err)
				return nil

M features.go => features.go +2 -2
@@ 252,7 252,7 @@ func getFeature(name xml.Name, features []StreamFeature) (feature StreamFeature,

func writeStreamFeatures(ctx context.Context, s *Session, features []StreamFeature) (list *streamFeaturesList, err error) {
	start := xml.StartElement{Name: xml.Name{Space: "", Local: "stream:features"}}
	w := s.TokenWriter()
	w := s.Encoder()
	defer w.Close()
	if err = w.EncodeToken(start); err != nil {
		return


@@ 288,7 288,7 @@ func writeStreamFeatures(ctx context.Context, s *Session, features []StreamFeatu
	if err = w.EncodeToken(start.End()); err != nil {
		return list, err
	}
	if err = w.Flush(); err != nil {
	if err = w.(xmlstream.Flusher).Flush(); err != nil {
		return list, err
	}
	return list, err

M handler.go => handler.go +3 -3
@@ 12,16 12,16 @@ import (

// A Handler triggers events or responds to incoming elements in an XML stream.
type Handler interface {
	HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error
	HandleXMPP(t xmlstream.DecodeEncoder, start *xml.StartElement) error
}

// The HandlerFunc type is an adapter to allow the use of ordinary functions as
// XMPP handlers.
// If f is a function with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error
type HandlerFunc func(t xmlstream.DecodeEncoder, start *xml.StartElement) error

// HandleXMPP calls f(t, start).
func (f HandlerFunc) HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (f HandlerFunc) HandleXMPP(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	return f(t, start)
}

M handler_test.go => handler_test.go +10 -8
@@ 11,17 11,19 @@ import (

var errHandlerFuncSentinal = errors.New("handler test")

type sentinalReadWriter struct{}
type sentinalDecodeEncoder struct{}

func (sentinalReadWriter) Token() (xml.Token, error)                         { return nil, nil }
func (sentinalReadWriter) EncodeToken(xml.Token) error                       { return nil }
func (sentinalReadWriter) Encode(interface{}) error                          { return nil }
func (sentinalReadWriter) EncodeElement(interface{}, xml.StartElement) error { return nil }
func (sentinalDecodeEncoder) Token() (xml.Token, error)                          { return nil, nil }
func (sentinalDecodeEncoder) Decode(interface{}) error                           { return nil }
func (sentinalDecodeEncoder) DecodeElement(interface{}, *xml.StartElement) error { return nil }
func (sentinalDecodeEncoder) EncodeToken(xml.Token) error                        { return nil }
func (sentinalDecodeEncoder) Encode(interface{}) error                           { return nil }
func (sentinalDecodeEncoder) EncodeElement(interface{}, xml.StartElement) error  { return nil }

func TestHandlerFunc(t *testing.T) {
	s := &xml.StartElement{}
	var f xmpp.HandlerFunc = func(r xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		if _, ok := r.(sentinalReadWriter); !ok {
	var f xmpp.HandlerFunc = func(r xmlstream.DecodeEncoder, start *xml.StartElement) error {
		if _, ok := r.(sentinalDecodeEncoder); !ok {
			t.Errorf("HandleXMPP did not pass reader to HandlerFunc")
		}
		if start != s {


@@ 30,7 32,7 @@ func TestHandlerFunc(t *testing.T) {
		return errHandlerFuncSentinal
	}

	err := f.HandleXMPP(sentinalReadWriter{}, s)
	err := f.HandleXMPP(sentinalDecodeEncoder{}, s)
	if err != errHandlerFuncSentinal {
		t.Errorf("HandleXMPP did not return handlerfunc error, got %q", err)
	}

M ibr2/ibr2.go => ibr2/ibr2.go +3 -3
@@ 133,9 133,9 @@ func decodeClientResp(ctx context.Context, r xml.TokenReader, decode func(ctx co
func negotiateFunc(challenges ...Challenge) func(context.Context, *xmpp.Session, interface{}) (xmpp.SessionState, io.ReadWriter, error) {
	return func(ctx context.Context, session *xmpp.Session, supported interface{}) (mask xmpp.SessionState, rw io.ReadWriter, err error) {
		server := (session.State() & xmpp.Received) == xmpp.Received
		w := session.TokenWriter()
		w := session.Encoder()
		defer w.Close()
		r := session.TokenReader()
		r := session.Decoder()
		defer r.Close()

		if !server && !supported.(bool) {


@@ 163,7 163,7 @@ func negotiateFunc(challenges ...Challenge) func(context.Context, *xmpp.Session,
				if err != nil {
					return
				}
				err = w.Flush()
				err = w.(xmlstream.Flusher).Flush()
				if err != nil {
					return
				}

M internal/xmpptest/session.go => internal/xmpptest/session.go +1 -1
@@ 23,7 23,7 @@ import (
func NopNegotiator(state xmpp.SessionState) xmpp.Negotiator {
	return func(ctx context.Context, s *xmpp.Session, data interface{}) (xmpp.SessionState, io.ReadWriter, interface{}, error) {
		// Pop the stream start token.
		rc := s.TokenReader()
		rc := s.Decoder()
		defer rc.Close()

		_, err := rc.Token()

M mux/mux.go => mux/mux.go +37 -45
@@ 8,6 8,7 @@ package mux // import "mellium.im/xmpp/mux"
import (
	"encoding/xml"
	"fmt"
	"io"
	"strings"

	"mellium.im/xmlstream"


@@ 17,6 18,21 @@ import (
	"mellium.im/xmpp/stanza"
)

func newDecodeEncoder(t xml.TokenReader, e xmlstream.Encoder) xmlstream.DecodeEncoder {
	d, ok := t.(xmlstream.Decoder)
	if !ok {
		d = xml.NewTokenDecoder(t)
	}

	return struct {
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		Decoder: d,
		Encoder: e,
	}
}

const (
	iqStanza   = "iq"
	msgStanza  = "message"


@@ 200,7 216,7 @@ func (m *ServeMux) PresenceHandler(typ stanza.PresenceType, payload xml.Name) (h
}

// HandleXMPP dispatches the request to the handler that most closely matches.
func (m *ServeMux) HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (m *ServeMux) HandleXMPP(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	h, _ := m.Handler(start.Name)
	return h.HandleXMPP(t, start)
}


@@ 311,11 327,11 @@ func HandleFunc(n xml.Name, h xmpp.HandlerFunc) Option {

type nopHandler struct{}

func (nopHandler) HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error { return nil }
func (nopHandler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error   { return nil }
func (nopHandler) HandlePresence(p stanza.Presence, t xmlstream.TokenReadEncoder) error   { return nil }
func (nopHandler) HandleXMPP(xmlstream.DecodeEncoder, *xml.StartElement) error   { return nil }
func (nopHandler) HandleMessage(stanza.Message, xmlstream.DecodeEncoder) error   { return nil }
func (nopHandler) HandlePresence(stanza.Presence, xmlstream.DecodeEncoder) error { return nil }

func (m *ServeMux) iqRouter(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (m *ServeMux) iqRouter(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	iq, err := stanza.NewIQ(*start)
	if err != nil {
		return err


@@ 324,18 340,17 @@ func (m *ServeMux) iqRouter(t xmlstream.TokenReadEncoder, start *xml.StartElemen
	// Limit the stream to the inside of the IQ element, don't allow handlers to
	// advance to the end token since they don't have access to the IQ start
	// token.
	t = struct {
		xml.TokenReader
		xmlstream.Encoder
	}{
		Encoder:     t,
		TokenReader: xmlstream.Inner(t),
	}
	t = newDecodeEncoder(xmlstream.Inner(t), t)
	tok, err := t.Token()
	if err != nil {
		return err
	}
	payloadStart, _ := tok.(xml.StartElement)
	payloadStart, ok := tok.(xml.StartElement)
	if !ok {
		// This is an empty IQ element, which is illegal so bail out and just return
		// io.EOF.
		return io.EOF
	}
	h, _ := m.IQHandler(iq.Type, payloadStart.Name)
	return h.HandleIQ(iq, t, &payloadStart)
}


@@ 378,7 393,7 @@ func (e multiErr) Error() string {
	return buf.String()
}

func (m *ServeMux) msgRouter(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (m *ServeMux) msgRouter(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	msg, err := stanza.NewMessage(*start)
	if err != nil {
		return err


@@ 387,7 402,7 @@ func (m *ServeMux) msgRouter(t xmlstream.TokenReadEncoder, start *xml.StartEleme
	return forChildren(m, msg, t, start)
}

func (m *ServeMux) presenceRouter(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (m *ServeMux) presenceRouter(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	presence, err := stanza.NewPresence(*start)
	if err != nil {
		return err


@@ 396,7 411,7 @@ func (m *ServeMux) presenceRouter(t xmlstream.TokenReadEncoder, start *xml.Start
	return forChildren(m, presence, t, start)
}

func forChildren(m *ServeMux, stanzaVal interface{}, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func forChildren(m *ServeMux, stanzaVal interface{}, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	r := &bufReader{
		r: t,
		// TODO: figure out a good buffer size


@@ 420,24 435,13 @@ func forChildren(m *ServeMux, stanzaVal interface{}, t xmlstream.TokenReadEncode
		case stanza.Presence:
			br := &bufReader{r: t, buf: r.buf}
			h, _ := m.PresenceHandler(s.Type, start.Name)
			err = h.HandlePresence(s, struct {
				xml.TokenReader
				xmlstream.Encoder
			}{
				TokenReader: br,
				Encoder:     t,
			})

			err = h.HandlePresence(s, newDecodeEncoder(br, t))
			r.buf = br.buf
		case stanza.Message:
			br := &bufReader{r: t, buf: r.buf}
			h, _ := m.MessageHandler(s.Type, start.Name)
			err = h.HandleMessage(s, struct {
				xml.TokenReader
				xmlstream.Encoder
			}{
				TokenReader: br,
				Encoder:     t,
			})
			err = h.HandleMessage(s, newDecodeEncoder(br, t))
			r.buf = br.buf
		}
		if err != nil {


@@ 457,28 461,16 @@ func forChildren(m *ServeMux, stanzaVal interface{}, t xmlstream.TokenReadEncode
		switch s := stanzaVal.(type) {
		case stanza.Presence:
			h, _ := m.PresenceHandler(s.Type, xml.Name{})
			return h.HandlePresence(s, struct {
				xml.TokenReader
				xmlstream.Encoder
			}{
				TokenReader: r,
				Encoder:     t,
			})
			return h.HandlePresence(s, newDecodeEncoder(r, t))
		case stanza.Message:
			h, _ := m.MessageHandler(s.Type, xml.Name{})
			return h.HandleMessage(s, struct {
				xml.TokenReader
				xmlstream.Encoder
			}{
				TokenReader: r,
				Encoder:     t,
			})
			return h.HandleMessage(s, newDecodeEncoder(r, t))
		}
	}
	return nil
}

func iqFallback(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func iqFallback(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	if iq.Type == stanza.ErrorIQ {
		return nil
	}

M mux/mux_test.go => mux/mux_test.go +22 -33
@@ 15,7 15,6 @@ import (
	"testing"

	"mellium.im/xmlstream"
	"mellium.im/xmpp/internal/marshal"
	"mellium.im/xmpp/internal/ns"
	"mellium.im/xmpp/internal/xmpptest"
	"mellium.im/xmpp/mux"


@@ 31,16 30,16 @@ const exampleNS = "com.example"

type passHandler struct{}

func (passHandler) HandleXMPP(xmlstream.TokenReadEncoder, *xml.StartElement) error   { return passTest }
func (passHandler) HandleMessage(stanza.Message, xmlstream.TokenReadEncoder) error   { return passTest }
func (passHandler) HandlePresence(stanza.Presence, xmlstream.TokenReadEncoder) error { return passTest }
func (passHandler) HandleIQ(stanza.IQ, xmlstream.TokenReadEncoder, *xml.StartElement) error {
func (passHandler) HandleXMPP(xmlstream.DecodeEncoder, *xml.StartElement) error   { return passTest }
func (passHandler) HandleMessage(stanza.Message, xmlstream.DecodeEncoder) error   { return passTest }
func (passHandler) HandlePresence(stanza.Presence, xmlstream.DecodeEncoder) error { return passTest }
func (passHandler) HandleIQ(stanza.IQ, xmlstream.DecodeEncoder, *xml.StartElement) error {
	return passTest
}

type multiHandler struct{}

func (multiHandler) HandlePresence(_ stanza.Presence, t xmlstream.TokenReadEncoder) error {
func (multiHandler) HandlePresence(_ stanza.Presence, t xmlstream.DecodeEncoder) error {
	d := xml.NewTokenDecoder(t)
	data := struct {
		stanza.Presence


@@ 61,7 60,7 @@ func (multiHandler) HandlePresence(_ stanza.Presence, t xmlstream.TokenReadEncod
	return passTest
}

func (m multiHandler) HandleMessage(_ stanza.Message, t xmlstream.TokenReadEncoder) error {
func (m multiHandler) HandleMessage(_ stanza.Message, t xmlstream.DecodeEncoder) error {
	d := xml.NewTokenDecoder(t)
	data := struct {
		stanza.Message


@@ 84,16 83,16 @@ func (m multiHandler) HandleMessage(_ stanza.Message, t xmlstream.TokenReadEncod

type failHandler struct{}

func (failHandler) HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (failHandler) HandleXMPP(t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	return failTest
}
func (failHandler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {
func (failHandler) HandleMessage(msg stanza.Message, t xmlstream.DecodeEncoder) error {
	return failTest
}
func (failHandler) HandlePresence(p stanza.Presence, t xmlstream.TokenReadEncoder) error {
func (failHandler) HandlePresence(p stanza.Presence, t xmlstream.DecodeEncoder) error {
	return failTest
}
func (failHandler) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (failHandler) HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	return failTest
}



@@ 101,8 100,7 @@ type decodeHandler struct {
	Body string
}

func (h decodeHandler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {
	d := xml.NewTokenDecoder(t)
func (h decodeHandler) HandleMessage(msg stanza.Message, d xmlstream.DecodeEncoder) error {
	data := struct {
		stanza.Message



@@ 475,7 473,7 @@ var testCases = [...]struct {
}

type nopEncoder struct {
	xml.TokenReader
	*xml.Decoder
}

func (nopEncoder) Encode(interface{}) error                          { return nil }


@@ 498,7 496,7 @@ func TestMux(t *testing.T) {
			tok, _ := d.Token()
			start := tok.(xml.StartElement)

			err := m.HandleXMPP(nopEncoder{TokenReader: d}, &start)
			err := m.HandleXMPP(nopEncoder{Decoder: d}, &start)
			switch {
			case tc.err == nil && err != nil:
				t.Fatalf("unexpected error: %v", err)


@@ 513,18 511,6 @@ func TestMux(t *testing.T) {
	}
}

type testEncoder struct {
	xml.TokenReader
	xmlstream.TokenWriter
}

func (e testEncoder) Encode(v interface{}) error {
	return marshal.EncodeXML(e.TokenWriter, v)
}
func (e testEncoder) EncodeElement(v interface{}, start xml.StartElement) error {
	return marshal.EncodeXMLElement(e.TokenWriter, v, start)
}

func TestFallback(t *testing.T) {
	buf := &bytes.Buffer{}
	rw := struct {


@@ 536,23 522,26 @@ func TestFallback(t *testing.T) {
	}
	s := xmpptest.NewSession(0, rw)

	r := s.TokenReader()
	r := s.Decoder()
	defer r.Close()
	tok, err := r.Token()
	if err != nil {
		t.Fatalf("Bad start token read: `%v'", err)
	}
	start := tok.(xml.StartElement)
	w := s.TokenWriter()
	w := s.Encoder()
	defer w.Close()
	err = mux.New().HandleXMPP(testEncoder{
		TokenReader: r,
		TokenWriter: w,
	err = mux.New().HandleXMPP(struct {
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		Decoder: r,
		Encoder: w,
	}, &start)
	if err != nil {
		t.Errorf("Unexpected error: `%v'", err)
	}
	if err := w.Flush(); err != nil {
	if err := w.(xmlstream.Flusher).Flush(); err != nil {
		t.Errorf("Unexpected error flushing token writer: %q", err)
	}


M mux/stanza.go => mux/stanza.go +9 -9
@@ 13,48 13,48 @@ import (

// IQHandler responds to IQ stanzas.
type IQHandler interface {
	HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error
	HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error
}

// The IQHandlerFunc type is an adapter to allow the use of ordinary functions
// as IQ handlers.
// If f is a function with the appropriate signature, IQHandlerFunc(f) is an
// IQHandler that calls f.
type IQHandlerFunc func(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error
type IQHandlerFunc func(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error

// HandleIQ calls f(iq, t, start).
func (f IQHandlerFunc) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (f IQHandlerFunc) HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	return f(iq, t, start)
}

// MessageHandler responds to message stanzas.
type MessageHandler interface {
	HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error
	HandleMessage(msg stanza.Message, t xmlstream.DecodeEncoder) error
}

// The MessageHandlerFunc type is an adapter to allow the use of ordinary
// functions as message handlers.
// If f is a function with the appropriate signature, MessageHandlerFunc(f) is a
// MessageHandler that calls f.
type MessageHandlerFunc func(msg stanza.Message, t xmlstream.TokenReadEncoder) error
type MessageHandlerFunc func(msg stanza.Message, t xmlstream.DecodeEncoder) error

// HandleMessage calls f(msg, t).
func (f MessageHandlerFunc) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {
func (f MessageHandlerFunc) HandleMessage(msg stanza.Message, t xmlstream.DecodeEncoder) error {
	return f(msg, t)
}

// PresenceHandler responds to message stanzas.
type PresenceHandler interface {
	HandlePresence(p stanza.Presence, t xmlstream.TokenReadEncoder) error
	HandlePresence(p stanza.Presence, t xmlstream.DecodeEncoder) error
}

// The PresenceHandlerFunc type is an adapter to allow the use of ordinary
// functions as presence handlers.
// If f is a function with the appropriate signature, PresenceHandlerFunc(f) is
// a PresenceHandler that calls f.
type PresenceHandlerFunc func(p stanza.Presence, t xmlstream.TokenReadEncoder) error
type PresenceHandlerFunc func(p stanza.Presence, t xmlstream.DecodeEncoder) error

// HandlePresence calls f(p, t).
func (f PresenceHandlerFunc) HandlePresence(p stanza.Presence, t xmlstream.TokenReadEncoder) error {
func (f PresenceHandlerFunc) HandlePresence(p stanza.Presence, t xmlstream.DecodeEncoder) error {
	return f(p, t)
}

M ping/ping.go => ping/ping.go +1 -1
@@ 28,7 28,7 @@ func Handle() mux.Option {
type Handler struct{}

// HandleIQ implements mux.IQHandler.
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	if iq.Type != stanza.GetIQ || start.Name.Local != "ping" || start.Name.Space != NS {
		return nil
	}

M ping/ping_test.go => ping/ping_test.go +14 -14
@@ 63,8 63,8 @@ func TestEncode(t *testing.T) {
	})
}

type tokenReadEncoder struct {
	xml.TokenReader
type decodeEncoder struct {
	xmlstream.Decoder
	xmlstream.Encoder
}



@@ 89,9 89,9 @@ func TestRoundTrip(t *testing.T) {
	e := xml.NewEncoder(&b)

	m := mux.New(ping.Handle())
	err = m.HandleXMPP(tokenReadEncoder{
		TokenReader: d,
		Encoder:     e,
	err = m.HandleXMPP(decodeEncoder{
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error handling ping: %v", err)


@@ 118,9 118,9 @@ func TestWrongIQType(t *testing.T) {
	start := tok.(xml.StartElement)

	m := mux.New(mux.IQ(stanza.SetIQ, xml.Name{Local: "ping", Space: ping.NS}, ping.Handler{}))
	err := m.HandleXMPP(tokenReadEncoder{
		TokenReader: d,
		Encoder:     e,
	err := m.HandleXMPP(decodeEncoder{
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error handling ping: %v", err)


@@ 144,9 144,9 @@ func TestBadPayloadLocalname(t *testing.T) {
	start := tok.(xml.StartElement)

	m := mux.New(mux.IQ(stanza.GetIQ, xml.Name{Local: "badlocal", Space: ping.NS}, ping.Handler{}))
	err := m.HandleXMPP(tokenReadEncoder{
		TokenReader: d,
		Encoder:     e,
	err := m.HandleXMPP(decodeEncoder{
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error handling ping: %v", err)


@@ 170,9 170,9 @@ func TestBadPayloadNamespace(t *testing.T) {
	start := tok.(xml.StartElement)

	m := mux.New(mux.IQ(stanza.GetIQ, xml.Name{Local: "ping", Space: "badnamespace"}, ping.Handler{}))
	err := m.HandleXMPP(tokenReadEncoder{
		TokenReader: d,
		Encoder:     e,
	err := m.HandleXMPP(decodeEncoder{
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error handling ping: %v", err)

M receipts/receipts.go => receipts/receipts.go +1 -1
@@ 56,7 56,7 @@ type Handler struct {

// HandleMessage implements mux.MessageHandler and responds to requests and
// responses for message delivery receipts.
func (h *Handler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {
func (h *Handler) HandleMessage(msg stanza.Message, t xmlstream.DecodeEncoder) error {
	// Pop the start message token
	_, err := t.Token()
	if err != nil {

M receipts/receipts_test.go => receipts/receipts_test.go +6 -6
@@ 47,11 47,11 @@ func TestClosedDoesNotPanic(t *testing.T) {
	// If the has not been removed from handling when the context is canceled,
	// this will panic (effectively failing the test).
	err = h.HandleMessage(msg, struct {
		xml.TokenReader
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		TokenReader: r,
		Encoder:     e,
		Decoder: xml.NewTokenDecoder(r),
		Encoder: e,
	})
	if err != nil {
		t.Fatalf("error handling response: %v", err)


@@ 85,11 85,11 @@ func TestRoundTrip(t *testing.T) {

	m := mux.New(receipts.Handle(h))
	err = m.HandleXMPP(struct {
		xml.TokenReader
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		TokenReader: d,
		Encoder:     e,
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error in handler: %v", err)

M roster/roster.go => roster/roster.go +1 -1
@@ 41,7 41,7 @@ type Handler struct {
}

// HandleIQ responds to roster push IQs.
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	item := Item{}
	err := xml.NewTokenDecoder(t).Decode(&item)
	if err != nil {

M roster/roster_test.go => roster/roster_test.go +3 -3
@@ 119,11 119,11 @@ func TestReceivePush(t *testing.T) {
	start := tok.(xml.StartElement)
	m := mux.New(roster.Handle(h))
	err = m.HandleXMPP(struct {
		xml.TokenReader
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		TokenReader: d,
		Encoder:     e,
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error in handler: %v", err)

M sasl.go => sasl.go +3 -4
@@ 135,9 135,8 @@ func SASL(identity, password string, mechanisms ...sasl.Mechanism) StreamFeature
				return mask, nil, err
			}

			r := session.TokenReader()
			defer r.Close()
			d := xml.NewTokenDecoder(r)
			d := session.Decoder()
			defer d.Close()

			// If we're already done after the first step, decode the <success/> or
			// <failure/> before we exit.


@@ 205,7 204,7 @@ func SASL(identity, password string, mechanisms ...sasl.Mechanism) StreamFeature
	}
}

func decodeSASLChallenge(d *xml.Decoder, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
func decodeSASLChallenge(d xmlstream.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 +2 -3
@@ 155,7 155,7 @@ func SASL(identity, password string, mechanisms ...sasl.Mechanism) xmpp.StreamFe
				return mask, nil, err
			}

			r := session.TokenReader()
			r := session.Decoder()
			defer r.Close()

			// If we're already done after the first step, decode the <success/> or


@@ 224,8 224,7 @@ func SASL(identity, password string, mechanisms ...sasl.Mechanism) xmpp.StreamFe
	}
}

func decodeSASLChallenge(r xml.TokenReader, start xml.StartElement, allowChallenge bool) (challenge []byte, success bool, err error) {
	d := xml.NewTokenDecoder(r)
func decodeSASLChallenge(d xmlstream.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 +44 -21
@@ 299,7 299,7 @@ func (s *Session) sendError(err error) (e error) {

type nopHandler struct{}

func (nopHandler) HandleXMPP(xmlstream.TokenReadEncoder, *xml.StartElement) error {
func (nopHandler) HandleXMPP(xmlstream.DecodeEncoder, *xml.StartElement) error {
	return nil
}



@@ 319,7 319,7 @@ func (r iqResponder) Close() error {

func handleInputStream(s *Session, handler Handler) (err error) {
	discard := xmlstream.Discard()
	rc := s.TokenReader()
	rc := s.Decoder()
	defer rc.Close()
	r := intstream.Reader(rc)



@@ 397,12 397,12 @@ func handleInputStream(s *Session, handler Handler) (err error) {

noreply:

	w := s.TokenWriter()
	w := s.Encoder()
	defer w.Close()
	rw := &responseChecker{
		TokenReader: xmlstream.MultiReader(xmlstream.Inner(r), xmlstream.Token(start.End())),
		TokenWriter: w,
		id:          id,
		Decoder: xml.NewTokenDecoder(xmlstream.MultiReader(xmlstream.Inner(r), xmlstream.Token(start.End()))),
		Encoder: w,
		id:      id,
	}
	if err := handler.HandleXMPP(rw, &start); err != nil {
		return err


@@ 422,7 422,7 @@ noreply:
		}
	}

	if err := w.Flush(); err != nil {
	if err := rw.Flush(); err != nil {
		return err
	}



@@ 433,8 433,8 @@ noreply:
}

type responseChecker struct {
	xml.TokenReader
	xmlstream.TokenWriter
	xmlstream.Decoder
	xmlstream.Encoder
	id        string
	wroteResp bool
	level     int


@@ 452,7 452,14 @@ func (rw *responseChecker) EncodeToken(t xml.Token) error {
		rw.level--
	}

	return rw.TokenWriter.EncodeToken(t)
	return rw.Encoder.EncodeToken(t)
}

func (rw *responseChecker) Flush() error {
	if f, ok := rw.Encoder.(xmlstream.Flusher); ok {
		return f.Flush()
	}
	return nil
}

func (rw *responseChecker) Encode(v interface{}) error {


@@ 498,6 505,14 @@ func (lwc *lockWriteCloser) EncodeToken(t xml.Token) error {
	return lwc.w.out.e.EncodeToken(t)
}

func (lwc *lockWriteCloser) Encode(v interface{}) error {
	return marshal.EncodeXML(lwc, v)
}

func (lwc *lockWriteCloser) EncodeElement(v interface{}, start xml.StartElement) error {
	return marshal.EncodeXMLElement(lwc, v, start)
}

func (lwc *lockWriteCloser) Flush() error {
	if lwc.err != nil {
		return nil


@@ 535,6 550,14 @@ func (lrc *lockReadCloser) Token() (xml.Token, error) {
	return lrc.s.in.d.Token()
}

func (lrc *lockReadCloser) Decode(v interface{}) error {
	return xml.NewTokenDecoder(lrc.s.in.d).Decode(v)
}

func (lrc *lockReadCloser) DecodeElement(v interface{}, start *xml.StartElement) error {
	return xml.NewTokenDecoder(lrc.s.in.d).DecodeElement(v, start)
}

func (lrc *lockReadCloser) Close() error {
	if lrc.err != nil {
		return nil


@@ 544,13 567,13 @@ func (lrc *lockReadCloser) Close() error {
	return nil
}

// TokenWriter returns a new xmlstream.TokenWriteCloser that can be used to
// write raw XML tokens to the session.
// All other writes and future calls to TokenWriter will block until the Close
// Encoder returns a new xmlstream.EncodeCloser that can be used to write XML
// tokens and elements to the session.
// All other writes and future calls to Encoder will block until the Close
// method is called.
// After the TokenWriteCloser has been closed, any future writes will return
// After the TokenEncodeCloser has been closed, any future writes will return
// io.EOF.
func (s *Session) TokenWriter() xmlstream.TokenWriteFlushCloser {
func (s *Session) Encoder() xmlstream.EncodeCloser {
	s.out.Lock()

	return &lockWriteCloser{


@@ 559,13 582,13 @@ func (s *Session) TokenWriter() xmlstream.TokenWriteFlushCloser {
	}
}

// TokenReader returns a new xmlstream.TokenReadCloser that can be used to read
// raw XML tokens from the session.
// All other reads and future calls to TokenReader will block until the Close
// method is called.
// After the TokenReadCloser has been closed, any future reads will return
// Decoder returns a new xmlstream.DecodeCloser that can be used to read XML
// tokens and elements from the session.
// All other reads and future calls to Decoder will block until the Close method
// is called.
// After the TokenDecodeCloser has been closed, any future reads will return
// io.EOF.
func (s *Session) TokenReader() xmlstream.TokenReadCloser {
func (s *Session) Decoder() xmlstream.DecodeCloser {
	s.in.Lock()

	return &lockReadCloser{

M session_test.go => session_test.go +15 -15
@@ 31,7 31,7 @@ func TestClosedInputStream(t *testing.T) {
			mask := xmpp.SessionState(i)
			buf := new(bytes.Buffer)
			s := xmpptest.NewSession(mask, buf)
			r := s.TokenReader()
			r := s.Decoder()
			defer r.Close()

			_, err := r.Token()


@@ 51,7 51,7 @@ func TestClosedOutputStream(t *testing.T) {
			mask := xmpp.SessionState(i)
			buf := new(bytes.Buffer)
			s := xmpptest.NewSession(mask, buf)
			w := s.TokenWriter()
			w := s.Encoder()
			defer w.Close()

			switch err := w.EncodeToken(xml.CharData("chartoken")); {


@@ 60,7 60,7 @@ func TestClosedOutputStream(t *testing.T) {
			case mask&xmpp.OutputStreamClosed == 0 && err != nil:
				t.Errorf("Unexpected error: `%v'", err)
			}
			switch err := w.Flush(); {
			switch err := w.(xmlstream.Flusher).Flush(); {
			case mask&xmpp.OutputStreamClosed == xmpp.OutputStreamClosed && err != xmpp.ErrOutputStreamClosed:
				t.Errorf("Unexpected error flushing: want=`%v', got=`%v'", xmpp.ErrOutputStreamClosed, err)
			case mask&xmpp.OutputStreamClosed == 0 && err != nil:


@@ 137,7 137,7 @@ func TestNegotiator(t *testing.T) {

const invalidIQ = `<iq type="error" id="1234"><error type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></service-unavailable></error></iq>`

var failHandler xmpp.HandlerFunc = func(r xmlstream.TokenReadEncoder, t *xml.StartElement) error {
var failHandler xmpp.HandlerFunc = func(r xmlstream.DecodeEncoder, t *xml.StartElement) error {
	return errors.New("session_test: FAILED")
}



@@ 161,7 161,7 @@ var serveTests = [...]struct {
		out: invalidIQ + `</stream:stream>`,
	},
	3: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "1234",
				Type: stanza.ResultIQ,


@@ 172,7 172,7 @@ var serveTests = [...]struct {
		out: `<iq type="result" id="1234"></iq></stream:stream>`,
	},
	4: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "wrongid",
				Type: stanza.ResultIQ,


@@ 183,7 183,7 @@ var serveTests = [...]struct {
		out: `<iq type="result" id="wrongid"></iq>` + invalidIQ + `</stream:stream>`,
	},
	5: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "1234",
				Type: stanza.ErrorIQ,


@@ 194,7 194,7 @@ var serveTests = [...]struct {
		out: `<iq type="error" id="1234"></iq></stream:stream>`,
	},
	6: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "1234",
				Type: stanza.GetIQ,


@@ 205,7 205,7 @@ var serveTests = [...]struct {
		out: `<iq type="get" id="1234"></iq>` + invalidIQ + `</stream:stream>`,
	},
	7: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			for _, attr := range start.Attr {
				if attr.Name.Local == "from" && attr.Value != "" {
					panic("expected attr to be normalized")


@@ 217,7 217,7 @@ var serveTests = [...]struct {
		out: `</stream:stream>`,
	},
	8: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			for _, attr := range start.Attr {
				if attr.Name.Local == "from" && attr.Value == "" {
					panic("expected attr not to be normalized")


@@ 229,7 229,7 @@ var serveTests = [...]struct {
		out: `</stream:stream>`,
	},
	9: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			for _, attr := range start.Attr {
				if attr.Name.Local == "from" && attr.Value == "" {
					panic("expected attr not to be normalized")


@@ 241,7 241,7 @@ var serveTests = [...]struct {
		out: `</stream:stream>`,
	},
	10: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			for _, attr := range start.Attr {
				if attr.Name.Local == "from" && attr.Value == "" {
					panic("expected attr not to be normalized")


@@ 258,7 258,7 @@ var serveTests = [...]struct {
		out:     `</stream:stream>`,
	},
	12: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			if start.Name.Space == stream.NS || start.Name.Space == "stream" {
				return fmt.Errorf("handler should never receive stream namespaced elements but got %v", start)
			}


@@ 269,7 269,7 @@ var serveTests = [...]struct {
		err: stream.InternalServerError,
	},
	13: {
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			if start.Name.Space == stream.NS || start.Name.Space == "stream" {
				return fmt.Errorf("handler should never receive stream namespaced elements but got %v", start)
			}


@@ 282,7 282,7 @@ var serveTests = [...]struct {
	14: {
		// Regression test to ensure that we can't advance beyond the end of the
		// current element and that the close element is included in the stream.
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		handler: xmpp.HandlerFunc(func(rw xmlstream.DecodeEncoder, start *xml.StartElement) error {
			if start.Name.Local == "b" {
				return nil
			}

M starttls.go => starttls.go +4 -5
@@ 53,9 53,8 @@ func StartTLS(required bool, cfg *tls.Config) StreamFeature {
		Negotiate: func(ctx context.Context, session *Session, data interface{}) (mask SessionState, rw io.ReadWriter, err error) {
			conn := session.Conn()
			state := session.State()
			r := session.TokenReader()
			defer r.Close()
			d := xml.NewTokenDecoder(r)
			d := session.Decoder()
			defer d.Close()

			// If no TLSConfig was specified, use a default config.
			if cfg == nil {


@@ 83,13 82,13 @@ func StartTLS(required bool, cfg *tls.Config) StreamFeature {
						return mask, nil, stream.UnsupportedStanzaType
					case tok.Name.Local == "proceed":
						// Skip the </proceed> token.
						if err = d.Skip(); err != nil {
						if err = xmlstream.Skip(d); err != nil {
							return mask, nil, stream.InvalidXML
						}
						rw = tls.Client(conn, cfg)
					case tok.Name.Local == "failure":
						// Skip the </failure> token.
						if err = d.Skip(); err != nil {
						if err = xmlstream.Skip(d); err != nil {
							err = stream.InvalidXML
						}
						// Failure is not an "error", it's expected behavior. Immediately

M xtime/time.go => xtime/time.go +1 -1
@@ 123,7 123,7 @@ type Handler struct {
}

// HandleIQ responds to entity time requests.
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
func (h Handler) HandleIQ(iq stanza.IQ, t xmlstream.DecodeEncoder, start *xml.StartElement) error {
	if iq.Type != stanza.GetIQ || start.Name.Local != "time" || start.Name.Space != NS {
		return nil
	}

M xtime/time_test.go => xtime/time_test.go +6 -8
@@ 28,11 28,6 @@ var (
	_ xmlstream.WriterTo  = xtime.Time{}
)

type tokenReadEncoder struct {
	xml.TokenReader
	xmlstream.Encoder
}

func TestRoundTrip(t *testing.T) {
	// TODO: this test will likely be shared between all IQ handler packages. Can
	// we provide a helper in xmpptest to automate it?


@@ 60,9 55,12 @@ func TestRoundTrip(t *testing.T) {
	e := xml.NewEncoder(&b)

	m := mux.New(xtime.Handle(h))
	err = m.HandleXMPP(tokenReadEncoder{
		TokenReader: d,
		Encoder:     e,
	err = m.HandleXMPP(struct {
		xmlstream.Decoder
		xmlstream.Encoder
	}{
		Decoder: d,
		Encoder: e,
	}, &start)
	if err != nil {
		t.Errorf("unexpected error in handler: %v", err)