Skip to content

Commit d8bb9e7

Browse files
committed
all: move stanza wrapping to methods
Previously to wrap a payload in a stanza you would use the functions WrapIQ, WrapMessage, and WrapPresence. Each of these took their respective stanza types and a payload. These have been moved to Wrap methods on the various stanza types that take a payload to make them easier to use in handlers where you already have the stanza. The down side is that these methods now exist on types that embed a stanza, which may be confusing since the payload will be ignored and only the stanza will be used. Signed-off-by: Sam Whited <[email protected]>
1 parent 75e32aa commit d8bb9e7

22 files changed

+71
-117
lines changed

Diff for: CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
## Unreleased
66

7+
### Breaking
8+
9+
- mux: move `Wrap{IQ,Presence,Message}` functions to methods on the stanza types
10+
- mux: new handler types and API
11+
12+
713
### Added
814

915
- mux: ability to select handlers by stanza payload

Diff for: bind.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ type bindIQ struct {
5252

5353
func (biq *bindIQ) TokenReader() xml.TokenReader {
5454
if biq.Err != nil {
55-
return stanza.WrapIQ(biq.IQ, xmlstream.Wrap(biq.Err.TokenReader(),
55+
return biq.Wrap(xmlstream.Wrap(biq.Err.TokenReader(),
5656
xml.StartElement{Name: xml.Name{Local: "bind", Space: ns.Bind}},
5757
))
5858
}
5959

60-
return stanza.WrapIQ(biq.IQ, xmlstream.Wrap(biq.Bind.TokenReader(),
60+
return biq.Wrap(xmlstream.Wrap(biq.Bind.TokenReader(),
6161
xml.StartElement{Name: xml.Name{Local: "bind", Space: ns.Bind}},
6262
))
6363
}

Diff for: echobot_example_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func Example_echobot() {
5656
}()
5757

5858
// Send initial presence to let the server know we want to receive messages.
59-
err = s.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil))
59+
err = s.Send(context.TODO(), stanza.Presence{Type: stanza.AvailablePresence}.Wrap(nil))
6060
if err != nil {
6161
log.Printf("Error sending initial presence: %q", err)
6262
return

Diff for: examples/echobot/echo.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func echo(ctx context.Context, addr, pass string, xmlIn, xmlOut io.Writer, logge
7171
}()
7272

7373
// Send initial presence to let the server know we want to receive messages.
74-
err = s.Send(ctx, stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil))
74+
err = s.Send(ctx, stanza.Presence{Type: stanza.AvailablePresence}.Wrap(nil))
7575
if err != nil {
7676
return fmt.Errorf("Error sending initial presence: %w", err)
7777
}

Diff for: examples/msgrepl/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func main() {
8787
}()
8888

8989
// Send initial presence to let the server know we want to receive messages.
90-
err = session.Send(ctx, stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil))
90+
err = session.Send(ctx, stanza.Presence{Type: stanza.AvailablePresence}.Wrap(nil))
9191
if err != nil {
9292
logger.Fatalf("Error sending initial presence: %w", err)
9393
}

Diff for: mux/mux.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,6 @@ func iqFallback(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElem
497497
Type: stanza.Cancel,
498498
Condition: stanza.ServiceUnavailable,
499499
}
500-
_, err := xmlstream.Copy(t, stanza.WrapIQ(iq, e.TokenReader()))
500+
_, err := xmlstream.Copy(t, iq.Wrap(e.TokenReader()))
501501
return err
502502
}

Diff for: mux/mux_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ func TestFallback(t *testing.T) {
518518
t.Errorf("Unexpected error flushing token writer: %q", err)
519519
}
520520

521-
const expected = `<iq type="error" to="[email protected]" from="[email protected]" id="123"><error type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></service-unavailable></error></iq>`
521+
const expected = `<iq xmlns="jabber:client" type="error" to="[email protected]" from="[email protected]" id="123"><error type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></service-unavailable></error></iq>`
522522
if buf.String() != expected {
523523
t.Errorf("Bad output:\nwant=`%v'\n got=`%v'", expected, buf.String())
524524
}

Diff for: ping/ping.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,5 @@ func (iq IQ) WriteXML(w xmlstream.TokenWriter) (n int, err error) {
8484
// TokenReader satisfies the xmlstream.Marshaler interface.
8585
func (iq IQ) TokenReader() xml.TokenReader {
8686
start := xml.StartElement{Name: xml.Name{Local: "ping", Space: NS}}
87-
return stanza.WrapIQ(
88-
iq.IQ,
89-
xmlstream.Wrap(nil, start),
90-
)
87+
return iq.Wrap(xmlstream.Wrap(nil, start))
9188
}

Diff for: ping/ping_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func TestRoundTrip(t *testing.T) {
104104
out := b.String()
105105
// TODO: figure out a better way to ignore randomly generated IDs.
106106
out = regexp.MustCompile(`id=".*?"`).ReplaceAllString(out, `id="123"`)
107-
const expected = `<iq type="result" from="[email protected]" id="123"></iq>`
107+
const expected = `<iq xmlns="jabber:client" type="result" from="[email protected]" id="123"></iq>`
108108
if out != expected {
109109
t.Errorf("got=%s, want=%s", out, expected)
110110
}

Diff for: roster/roster.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (iq IQ) TokenReader() xml.TokenReader {
158158
iq.IQ.Type = stanza.GetIQ
159159
}
160160

161-
return stanza.WrapIQ(iq.IQ, iq.payload())
161+
return iq.IQ.Wrap(iq.payload())
162162
}
163163

164164
// Payload returns a stream of XML tokekns that match the roster query payload

Diff for: send_test.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ var sendIQTests = [...]struct {
5959
0: {
6060
iq: stanza.IQ{ID: testIQID, Type: stanza.GetIQ},
6161
writesBody: true,
62-
resp: stanza.WrapIQ(stanza.IQ{ID: testIQID, Type: stanza.ResultIQ}, nil),
62+
resp: stanza.IQ{ID: testIQID, Type: stanza.ResultIQ}.Wrap(nil),
6363
},
6464
1: {
6565
iq: stanza.IQ{ID: testIQID, Type: stanza.SetIQ},
6666
writesBody: true,
67-
resp: stanza.WrapIQ(stanza.IQ{ID: testIQID, Type: stanza.ErrorIQ}, nil),
67+
resp: stanza.IQ{ID: testIQID, Type: stanza.ErrorIQ}.Wrap(nil),
6868
},
6969
2: {
7070
iq: stanza.IQ{Type: stanza.ResultIQ, ID: testIQID},
@@ -168,7 +168,7 @@ func TestSendIQ(t *testing.T) {
168168
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
169169
defer cancel()
170170

171-
resp, err := s.SendIQ(ctx, stanza.WrapIQ(tc.iq, tc.payload))
171+
resp, err := s.SendIQ(ctx, tc.iq.Wrap(tc.payload))
172172
if err != tc.err {
173173
t.Errorf("Unexpected error, want=%q, got=%q", tc.err, err)
174174
}
@@ -215,19 +215,19 @@ var sendTests = [...]struct {
215215
err: xmpp.ErrNotStart,
216216
},
217217
2: {
218-
r: stanza.WrapMessage(to, stanza.NormalMessage, nil),
218+
r: stanza.Message{To: to, Type: stanza.NormalMessage}.Wrap(nil),
219219
writesBody: true,
220220
},
221221
3: {
222-
r: stanza.WrapPresence(to, stanza.AvailablePresence, nil),
222+
r: stanza.Presence{To: to, Type: stanza.AvailablePresence}.Wrap(nil),
223223
writesBody: true,
224224
},
225225
4: {
226-
r: stanza.WrapIQ(stanza.IQ{Type: stanza.ResultIQ}, nil),
226+
r: stanza.IQ{Type: stanza.ResultIQ}.Wrap(nil),
227227
writesBody: true,
228228
},
229229
5: {
230-
r: stanza.WrapIQ(stanza.IQ{Type: stanza.ErrorIQ}, nil),
230+
r: stanza.IQ{Type: stanza.ErrorIQ}.Wrap(nil),
231231
writesBody: true,
232232
},
233233
}

Diff for: session.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,10 @@ noreply:
410410

411411
// If the user did not write a response to an IQ, send a default one.
412412
if needsResp && !rw.wroteResp {
413-
_, err := xmlstream.Copy(w, stanza.WrapIQ(stanza.IQ{
413+
_, err := xmlstream.Copy(w, stanza.IQ{
414414
ID: id,
415415
Type: stanza.ErrorIQ,
416-
}, stanza.Error{
416+
}.Wrap(stanza.Error{
417417
Type: stanza.Cancel,
418418
Condition: stanza.ServiceUnavailable,
419419
}.TokenReader()))
@@ -794,12 +794,12 @@ func (s *Session) SendIQElement(ctx context.Context, payload xml.TokenReader, iq
794794

795795
// If this an IQ of type "set" or "get" we expect a response.
796796
if needsResp {
797-
return s.sendResp(ctx, iq.ID, stanza.WrapIQ(iq, payload))
797+
return s.sendResp(ctx, iq.ID, iq.Wrap(payload))
798798
}
799799

800800
// If this is an IQ of type result or error, we don't expect a response so
801801
// just send it normally.
802-
return nil, s.Send(ctx, stanza.WrapIQ(iq, payload))
802+
return nil, s.Send(ctx, iq.Wrap(payload))
803803
}
804804

805805
func (s *Session) sendResp(ctx context.Context, id string, payload xml.TokenReader) (xmlstream.TokenReadCloser, error) {

Diff for: session_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -162,43 +162,43 @@ var serveTests = [...]struct {
162162
},
163163
3: {
164164
handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
165-
_, err := xmlstream.Copy(rw, stanza.WrapIQ(stanza.IQ{
165+
_, err := xmlstream.Copy(rw, stanza.IQ{
166166
ID: "1234",
167167
Type: stanza.ResultIQ,
168-
}, nil))
168+
}.Wrap(nil))
169169
return err
170170
}),
171171
in: `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,
172172
out: `<iq type="result" id="1234"></iq></stream:stream>`,
173173
},
174174
4: {
175175
handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
176-
_, err := xmlstream.Copy(rw, stanza.WrapIQ(stanza.IQ{
176+
_, err := xmlstream.Copy(rw, stanza.IQ{
177177
ID: "wrongid",
178178
Type: stanza.ResultIQ,
179-
}, nil))
179+
}.Wrap(nil))
180180
return err
181181
}),
182182
in: `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,
183183
out: `<iq type="result" id="wrongid"></iq>` + invalidIQ + `</stream:stream>`,
184184
},
185185
5: {
186186
handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
187-
_, err := xmlstream.Copy(rw, stanza.WrapIQ(stanza.IQ{
187+
_, err := xmlstream.Copy(rw, stanza.IQ{
188188
ID: "1234",
189189
Type: stanza.ErrorIQ,
190-
}, nil))
190+
}.Wrap(nil))
191191
return err
192192
}),
193193
in: `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,
194194
out: `<iq type="error" id="1234"></iq></stream:stream>`,
195195
},
196196
6: {
197197
handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
198-
_, err := xmlstream.Copy(rw, stanza.WrapIQ(stanza.IQ{
198+
_, err := xmlstream.Copy(rw, stanza.IQ{
199199
ID: "1234",
200200
Type: stanza.GetIQ,
201-
}, nil))
201+
}.Wrap(nil))
202202
return err
203203
}),
204204
in: `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,

Diff for: stanza/example_pingstream_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// a ping payload.
1919
func WrapPingIQ(to jid.JID) xml.TokenReader {
2020
start := xml.StartElement{Name: xml.Name{Local: "ping", Space: "urn:xmpp:ping"}}
21-
return stanza.WrapIQ(stanza.IQ{To: to, Type: stanza.GetIQ}, xmlstream.Wrap(nil, start))
21+
return stanza.IQ{To: to, Type: stanza.GetIQ}.Wrap(xmlstream.Wrap(nil, start))
2222
}
2323

2424
func Example_stream() {

Diff for: stanza/iq.go

+15-46
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,6 @@ import (
1212
"mellium.im/xmpp/jid"
1313
)
1414

15-
// WrapIQ wraps a payload in an IQ stanza.
16-
// The resulting IQ may not contain an id or from attribute and thus may not be
17-
// valid without further processing.
18-
func WrapIQ(iq IQ, payload xml.TokenReader) xml.TokenReader {
19-
attr := []xml.Attr{
20-
{Name: xml.Name{Local: "type"}, Value: string(iq.Type)},
21-
}
22-
23-
if !iq.To.Equal(jid.JID{}) {
24-
to, err := iq.To.MarshalXMLAttr(xml.Name{Space: "", Local: "to"})
25-
if err == nil && to.Value != "" {
26-
attr = append(attr, to)
27-
}
28-
}
29-
if !iq.From.Equal(jid.JID{}) {
30-
from, err := iq.From.MarshalXMLAttr(xml.Name{Space: "", Local: "from"})
31-
if err == nil && from.Value != "" {
32-
attr = append(attr, from)
33-
}
34-
}
35-
36-
if iq.Lang != "" {
37-
attr = append(attr, xml.Attr{Name: xml.Name{Local: "lang", Space: ns.XML}, Value: iq.Lang})
38-
}
39-
if iq.ID != "" {
40-
attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: iq.ID})
41-
}
42-
43-
return xmlstream.Wrap(payload, xml.StartElement{
44-
Name: xml.Name{Local: "iq"},
45-
Attr: attr,
46-
})
47-
}
48-
4915
// IQ ("Information Query") is used as a general request response mechanism.
5016
// IQ's are one-to-one, provide get and set semantics, and always require a
5117
// response in the form of a result or an error.
@@ -74,38 +40,41 @@ func (iq IQ) StartElement() xml.StartElement {
7440
name.Local = "iq"
7541

7642
attr := make([]xml.Attr, 0, 5)
77-
if iq.ID != "" {
78-
attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: iq.ID})
79-
}
43+
attr = append(attr, xml.Attr{Name: xml.Name{Local: "type"}, Value: string(iq.Type)})
8044
if !iq.To.Equal(jid.JID{}) {
8145
attr = append(attr, xml.Attr{Name: xml.Name{Local: "to"}, Value: iq.To.String()})
8246
}
8347
if !iq.From.Equal(jid.JID{}) {
8448
attr = append(attr, xml.Attr{Name: xml.Name{Local: "from"}, Value: iq.From.String()})
8549
}
50+
if iq.ID != "" {
51+
attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: iq.ID})
52+
}
8653
if iq.Lang != "" {
8754
attr = append(attr, xml.Attr{Name: xml.Name{Space: ns.XML, Local: "lang"}, Value: iq.Lang})
8855
}
89-
if iq.Type != "" {
90-
attr = append(attr, xml.Attr{Name: xml.Name{Local: "type"}, Value: string(iq.Type)})
91-
}
9256

9357
return xml.StartElement{
9458
Name: name,
9559
Attr: attr,
9660
}
9761
}
9862

63+
// Wrap wraps the payload in a stanza.
64+
//
65+
// The resulting IQ may not contain an id or from attribute and thus may not be
66+
// valid without further processing.
67+
func (iq IQ) Wrap(payload xml.TokenReader) xml.TokenReader {
68+
return xmlstream.Wrap(payload, iq.StartElement())
69+
}
70+
9971
// Result returns a token reader that wraps the first element from payload in an
10072
// IQ stanza with the to and from attributes switched and the type set to
10173
// ResultIQ.
10274
func (iq IQ) Result(payload xml.TokenReader) xml.TokenReader {
103-
return WrapIQ(IQ{
104-
ID: iq.ID,
105-
To: iq.From,
106-
From: iq.To,
107-
Type: ResultIQ,
108-
}, payload)
75+
iq.Type = ResultIQ
76+
iq.From, iq.To = iq.To, iq.From
77+
return iq.Wrap(payload)
10978
}
11079

11180
// IQType is the type of an IQ stanza.

Diff for: stanza/iq_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func TestIQ(t *testing.T) {
4848
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
4949
b := new(bytes.Buffer)
5050
e := xml.NewEncoder(b)
51-
iq := stanza.WrapIQ(stanza.IQ{To: jid.MustParse(tc.to), Type: tc.typ}, tc.payload)
51+
iq := stanza.IQ{To: jid.MustParse(tc.to), Type: tc.typ}.Wrap(tc.payload)
5252
if _, err := xmlstream.Copy(e, iq); err != tc.err {
5353
t.Errorf("Unexpected error: want=`%v', got=`%v'", tc.err, err)
5454
}

Diff for: stanza/message.go

+6-14
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@ import (
1212
"mellium.im/xmpp/jid"
1313
)
1414

15-
// WrapMessage wraps a payload in a message stanza.
16-
func WrapMessage(to jid.JID, typ MessageType, payload xml.TokenReader) xml.TokenReader {
17-
return xmlstream.Wrap(payload, xml.StartElement{
18-
Name: xml.Name{Local: "message"},
19-
Attr: []xml.Attr{
20-
{Name: xml.Name{Local: "to"}, Value: to.String()},
21-
{Name: xml.Name{Local: "type"}, Value: string(typ)},
22-
},
23-
})
24-
}
25-
2615
// Message is an XMPP stanza that contains a payload for direct one-to-one
2716
// communication with another network entity. It is often used for sending chat
2817
// messages to an individual or group chat server, or for notifications and
@@ -52,6 +41,7 @@ func (msg Message) StartElement() xml.StartElement {
5241
name.Local = "message"
5342

5443
attr := make([]xml.Attr, 0, 5)
44+
attr = append(attr, xml.Attr{Name: xml.Name{Local: "type"}, Value: string(msg.Type)})
5545
if msg.ID != "" {
5646
attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: msg.ID})
5747
}
@@ -64,16 +54,18 @@ func (msg Message) StartElement() xml.StartElement {
6454
if msg.Lang != "" {
6555
attr = append(attr, xml.Attr{Name: xml.Name{Space: ns.XML, Local: "lang"}, Value: msg.Lang})
6656
}
67-
if msg.Type != "" {
68-
attr = append(attr, xml.Attr{Name: xml.Name{Local: "type"}, Value: string(msg.Type)})
69-
}
7057

7158
return xml.StartElement{
7259
Name: name,
7360
Attr: attr,
7461
}
7562
}
7663

64+
// Wrap wraps the payload in a stanza.
65+
func (msg Message) Wrap(payload xml.TokenReader) xml.TokenReader {
66+
return xmlstream.Wrap(payload, msg.StartElement())
67+
}
68+
7769
// MessageType is the type of a message stanza.
7870
// It should normally be one of the constants defined in this package.
7971
type MessageType string

0 commit comments

Comments
 (0)