Fix session element parsing on IQ
This commit is contained in:
parent
40e907e8ee
commit
cca0919b8a
|
@ -86,7 +86,28 @@ func TestClient_FeaturesTracking(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mock.Stop()
|
mock.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_RFC3921Session(t *testing.T) {
|
||||||
|
// Setup Mock server
|
||||||
|
mock := ServerMock{}
|
||||||
|
mock.Start(t, testXMPPAddress, handlerConnectWithSession)
|
||||||
|
|
||||||
|
// Test / Check result
|
||||||
|
config := Config{Address: testXMPPAddress, Jid: "test@localhost", Password: "test", Insecure: true}
|
||||||
|
|
||||||
|
var client *Client
|
||||||
|
var err error
|
||||||
|
router := NewRouter()
|
||||||
|
if client, err = NewClient(config, router); err != nil {
|
||||||
|
t.Errorf("connect create XMPP client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.Connect(); err != nil {
|
||||||
|
t.Errorf("XMPP connection failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
@ -94,6 +115,7 @@ func TestClient_FeaturesTracking(t *testing.T) {
|
||||||
|
|
||||||
const serverStreamOpen = "<?xml version='1.0'?><stream:stream to='%s' id='%s' xmlns='%s' xmlns:stream='%s' version='1.0'>"
|
const serverStreamOpen = "<?xml version='1.0'?><stream:stream to='%s' id='%s' xmlns='%s' xmlns:stream='%s' version='1.0'>"
|
||||||
|
|
||||||
|
// Test connection with a basic straightforward workflow
|
||||||
func handlerConnectSuccess(t *testing.T, c net.Conn) {
|
func handlerConnectSuccess(t *testing.T, c net.Conn) {
|
||||||
decoder := xml.NewDecoder(c)
|
decoder := xml.NewDecoder(c)
|
||||||
checkOpenStream(t, c, decoder)
|
checkOpenStream(t, c, decoder)
|
||||||
|
@ -114,6 +136,21 @@ func handlerAbortTLS(t *testing.T, c net.Conn) {
|
||||||
sendStreamFeatures(t, c, decoder) // Send initial features
|
sendStreamFeatures(t, c, decoder) // Send initial features
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test connection with mandatory session (RFC-3921)
|
||||||
|
func handlerConnectWithSession(t *testing.T, c net.Conn) {
|
||||||
|
decoder := xml.NewDecoder(c)
|
||||||
|
checkOpenStream(t, c, decoder)
|
||||||
|
|
||||||
|
sendStreamFeatures(t, c, decoder) // Send initial features
|
||||||
|
readAuth(t, decoder)
|
||||||
|
fmt.Fprintln(c, "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>")
|
||||||
|
|
||||||
|
checkOpenStream(t, c, decoder) // Reset stream
|
||||||
|
sendRFC3921Feature(t, c, decoder) // Send post auth features
|
||||||
|
bind(t, c, decoder)
|
||||||
|
session(t, c, decoder)
|
||||||
|
}
|
||||||
|
|
||||||
func checkOpenStream(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
func checkOpenStream(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
||||||
c.SetDeadline(time.Now().Add(defaultTimeout))
|
c.SetDeadline(time.Now().Add(defaultTimeout))
|
||||||
defer c.SetDeadline(time.Time{})
|
defer c.SetDeadline(time.Time{})
|
||||||
|
@ -176,7 +213,7 @@ func readAuth(t *testing.T, decoder *xml.Decoder) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendBindFeature(t *testing.T, c net.Conn, _ *xml.Decoder) {
|
func sendBindFeature(t *testing.T, c net.Conn, _ *xml.Decoder) {
|
||||||
// This is a basic server, supporting only 1 stream feature after auth: session binding
|
// This is a basic server, supporting only 1 stream feature after auth: resource binding
|
||||||
features := `<stream:features>
|
features := `<stream:features>
|
||||||
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
||||||
</stream:features>`
|
</stream:features>`
|
||||||
|
@ -185,6 +222,17 @@ func sendBindFeature(t *testing.T, c net.Conn, _ *xml.Decoder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendRFC3921Feature(t *testing.T, c net.Conn, _ *xml.Decoder) {
|
||||||
|
// This is a basic server, supporting only 2 features after auth: resource & session binding
|
||||||
|
features := `<stream:features>
|
||||||
|
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
||||||
|
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
|
||||||
|
</stream:features>`
|
||||||
|
if _, err := fmt.Fprintln(c, features); err != nil {
|
||||||
|
t.Errorf("cannot send stream feature: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
||||||
se, err := stanza.NextStart(decoder)
|
se, err := stanza.NextStart(decoder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -210,3 +258,24 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
||||||
fmt.Fprintf(c, result, iq.Id, "test@localhost/test") // TODO use real JID
|
fmt.Fprintf(c, result, iq.Id, "test@localhost/test") // TODO use real JID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func session(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
||||||
|
se, err := stanza.NextStart(decoder)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot read session: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iq := &stanza.IQ{}
|
||||||
|
// Decode element into pointer storage
|
||||||
|
if err = decoder.DecodeElement(&iq, &se); err != nil {
|
||||||
|
t.Errorf("cannot decode session iq: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch iq.Payload.(type) {
|
||||||
|
case *stanza.StreamSession:
|
||||||
|
result := `<iq id='%s' type='result'/>`
|
||||||
|
fmt.Fprintf(c, result, iq.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ func (s *Session) rfc3921Session(o Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var iq stanza.IQ
|
var iq stanza.IQ
|
||||||
if s.Features.Session.XMLName.Local == "session" && !s.Features.Session.Optional {
|
if !s.Features.Session.IsOptional() {
|
||||||
fmt.Fprintf(s.streamLogger, "<iq type='set' id='%s'><session xmlns='%s'/></iq>", s.PacketId(), stanza.NSSession)
|
fmt.Fprintf(s.streamLogger, "<iq type='set' id='%s'><session xmlns='%s'/></iq>", s.PacketId(), stanza.NSSession)
|
||||||
if s.err = s.decoder.Decode(&iq); s.err != nil {
|
if s.err = s.decoder.Decode(&iq); s.err != nil {
|
||||||
s.err = errors.New("expecting iq result after session open: " + s.err.Error())
|
s.err = errors.New("expecting iq result after session open: " + s.err.Error())
|
||||||
|
|
|
@ -95,10 +95,17 @@ func (s *StreamSession) Namespace() string {
|
||||||
return s.XMLName.Space
|
return s.XMLName.Space
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StreamSession) IsOptional() bool {
|
||||||
|
if s.XMLName.Local == "session" {
|
||||||
|
return s.Optional
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Registry init
|
// Registry init
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
TypeRegistry.MapExtension(PKTIQ, xml.Name{"urn:ietf:params:xml:ns:xmpp-bind", "bind"}, Bind{})
|
TypeRegistry.MapExtension(PKTIQ, xml.Name{"urn:ietf:params:xml:ns:xmpp-bind", "bind"}, Bind{})
|
||||||
TypeRegistry.MapExtension(PKTIQ, xml.Name{"urn:ietf:params:xml:ns:xmpp-session", "bind"}, StreamSession{})
|
TypeRegistry.MapExtension(PKTIQ, xml.Name{"urn:ietf:params:xml:ns:xmpp-session", "session"}, StreamSession{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check that we can detect optional session from advertised stream features
|
// Check that we can detect optional session from advertised stream features
|
||||||
func TestSession(t *testing.T) {
|
func TestSessionFeatures(t *testing.T) {
|
||||||
streamFeatures := stanza.StreamFeatures{Session: stanza.StreamSession{Optional: true}}
|
streamFeatures := stanza.StreamFeatures{Session: stanza.StreamSession{Optional: true}}
|
||||||
|
|
||||||
data, err := xml.Marshal(streamFeatures)
|
data, err := xml.Marshal(streamFeatures)
|
||||||
|
@ -18,10 +18,38 @@ func TestSession(t *testing.T) {
|
||||||
|
|
||||||
parsedStream := stanza.StreamFeatures{}
|
parsedStream := stanza.StreamFeatures{}
|
||||||
if err = xml.Unmarshal(data, &parsedStream); err != nil {
|
if err = xml.Unmarshal(data, &parsedStream); err != nil {
|
||||||
t.Errorf("Unmarshal(%s) returned error", data)
|
t.Errorf("Unmarshal(%s) returned error: %s", data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !parsedStream.Session.Optional {
|
if !parsedStream.Session.IsOptional() {
|
||||||
|
t.Error("Session should be optional")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the Session tag can be used in IQ decoding
|
||||||
|
func TestSessionIQ(t *testing.T) {
|
||||||
|
iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeSet, Id: "session"})
|
||||||
|
iq.Payload = &stanza.StreamSession{XMLName: xml.Name{Local: "session"}, Optional: true}
|
||||||
|
|
||||||
|
data, err := xml.Marshal(iq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot marshal xml structure: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedIQ := stanza.IQ{}
|
||||||
|
if err = xml.Unmarshal(data, &parsedIQ); err != nil {
|
||||||
|
t.Errorf("Unmarshal(%s) returned error: %s", data, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session, ok := parsedIQ.Payload.(*stanza.StreamSession)
|
||||||
|
if !ok {
|
||||||
|
t.Error("Missing session payload")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !session.IsOptional() {
|
||||||
t.Error("Session should be optional")
|
t.Error("Session should be optional")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue