~samwhited/xmpp

ref: e8c09b3ff1c1489c21d96a0f7f8f0e8728fc095a xmpp/s2s/bidi.go -rw-r--r-- 2.1 KiB
e8c09b3fSam Whited design: fix typo in design doc template 1 year, 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Copyright 2020 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

package s2s

import (
	"context"
	"encoding/xml"
	"io"

	"mellium.im/xmlstream"
	"mellium.im/xmpp"
)

// Namespaces used in this package, provided as a convenience.
const (
	// NSBidi is the namespace used for negotiating bidirectional S2S connections.
	NSBidi = "urn:xmpp:bidi"

	// NSBidiFeature is the namespace used for advertising Bidi support.
	NSBidiFeature = "urn:xmpp:features:bidi"
)

// Bidi returns a stream feature for indicating support for bidirectional
// server-to-server connections (ie. s2s connections over a single bidirectional
// TCP connection instead of over two unidirectional TCP connections).
//
// The feature itself is just informational, servers using this feature will
// need to check if it was negotiated and handle their connections
// appropriately.
func Bidi() xmpp.StreamFeature {
	return xmpp.StreamFeature{
		Name:       xml.Name{Space: NSBidiFeature, Local: "bidi"},
		Necessary:  xmpp.Secure,
		Prohibited: xmpp.Authn,
		List: func(ctx context.Context, e xmlstream.TokenWriter, start xml.StartElement) (bool, error) {
			if err := e.EncodeToken(start); err != nil {
				return false, err
			}
			return false, e.EncodeToken(start.End())
		},
		Parse: func(ctx context.Context, d *xml.Decoder, start *xml.StartElement) (bool, interface{}, error) {
			parsed := struct {
				XMLName xml.Name `xml:"urn:xmpp:features:bidi bidi"`
			}{}
			return false, nil, d.DecodeElement(&parsed, start)
		},
		Negotiate: func(ctx context.Context, session *xmpp.Session, data interface{}) (xmpp.SessionState, io.ReadWriter, error) {
			if (session.State() & xmpp.Received) == xmpp.Received {
				// The BIDI feature is just informational at this point, no need to
				// respond if we're a server.
				return 0, nil, nil
			}

			w := session.TokenWriter()
			defer w.Close()

			start := xml.StartElement{
				Name: xml.Name{Space: NSBidi, Local: "bidi"},
			}
			err := w.EncodeToken(start)
			if err != nil {
				return 0, nil, err
			}
			return 0, nil, w.EncodeToken(start.End())
		},
	}
}