27 Commits

Author SHA1 Message Date
Matthew Wild bc2b7ceee4 client: Notify session when explicitly disconnected
Run tests / test (push) Has been cancelled
2026-06-29 09:36:39 +02:00
Matthew Wild 240e7f5bff client: Notify transport when stream close is received 2026-06-29 09:36:39 +02:00
Matthew Wild b407d5cd11 xmpp_transport: Add buffer to closeChan to prevent deadlock
closeChan is used to communicate that a stream close has been received.

In the case where the client initiates the stream closure, this works:

1) client sends stream close
2) client reads from closeChan, waiting for the server's stream close

In the case where the server initiates the stream closure, it failed:

1) transport writes to closeChan
x) client is not waiting on closeChan, so the write blocks and transport hangs

This change allows up to one event to be written asynchronously to closeChan.
After the client is notified about the disconnection, it will read from
closeChan as in the client-initiated flow but will simply find the stream
close already there.
2026-06-29 09:36:39 +02:00
Matthew Wild 7df984516d xmpp_transport: Reset encryption status when initiating new connection
If the transport has been previously connected, the isSecure flag may still
be set from a previous connection. This can lead to not negotiating TLS on the
new connection. In practice this caused a stream error, because the post-TLS
stream reset was still sent unconditionally.

This change makes reconnecting a transport negotiate TLS successfully.
2026-06-29 09:36:39 +02:00
COD3HUNT3R 784ff58cc2 Merge pull request #186
* Updated the xmpp.NewClient call in the README.md example to pass the config parameter as a pointer (&config) instead of a value.
2025-09-29 15:11:55 +02:00
Bohdan Horbeshko 9b815bbbcb Get rid of CDATA around RSM first element content 2025-09-29 15:06:16 +02:00
Bohdan Horbeshko 4073f34461 Fix serialization of RSM first element 2025-09-29 15:06:16 +02:00
Mickaël Rémond 6973779b17 Fix versions 2025-09-29 14:52:48 +02:00
Mickaël Rémond c6fce730b7 Update Github action to Go 1.25 2025-09-29 14:01:44 +02:00
Mickaël Rémond 6365fcb59f Update other dependencies 2025-09-29 14:01:44 +02:00
Mickaël Rémond 8e1c4f1204 Bump Go version and go-cmp version
Update code to fix tests.
2025-09-29 14:01:44 +02:00
Mickaël Rémond 7186c058fd Update test.yaml 2024-05-16 17:41:31 +02:00
Mickaël Rémond 87fb1dfe78 Update test.yaml 2024-05-16 16:51:07 +02:00
Bohdan Horbeshko 655f875918 Support multiple command elements 2024-05-16 16:47:09 +02:00
Bohdan Horbeshko 9af32ad7e0 Fix marshalling/unmarshalling of command children 2024-05-16 16:47:09 +02:00
bodqhrohro 5f99e1cd06 Support partial JIDs in Bare/Full methods 2021-12-14 12:01:36 +01:00
remicorniere ac5b066815 Merge pull request #164 from remicorniere/XEP-0082
Support for XEP-0082.
2020-05-07 00:10:55 +00:00
CORNIERE Rémi 17d561f829 Support for XEP-0082.
Parsing of times with an offset does not work for now (should it ?)
2020-04-29 10:13:31 +02:00
remicorniere ce71bc5c76 Merge pull request #163 from remicorniere/XEP-0334
Support for XEP-0334Support for XEP-0334 (Message Hints)
2020-04-16 17:26:46 +02:00
CORNIERE Rémi 6a3ee5b0a5 Support for XEP-0334 2020-04-09 10:02:11 +02:00
remicorniere fe4c366de8 Merge pull request #161 from remicorniere/Issue_160
Fix issue #160
2020-03-16 22:29:03 +00:00
CORNIERE Rémi 0a4acd12c3 Fix issue #160 2020-03-16 16:20:54 +01:00
remicorniere ef6de6000a Merge pull request #159 from remicorniere/close_keepalive
Close keepalive when recv() ends in client.go
2020-03-16 11:13:13 +00:00
rcorniere d4960afc97 Close keepalive when recv() ends in client.go 2020-03-10 18:01:47 +01:00
rcorniere 6e84084bb3 Close keepalive when recv() ends in client.go 2020-03-10 17:32:03 +01:00
remicorniere 64e54134df Merge pull request #158 from remicorniere/reset_IsSecure
Reset isSecure in StartTLS()
2020-03-10 16:16:49 +00:00
rcorniere f1331dcebc Reset isSecure in StartTLS() 2020-03-10 17:14:26 +01:00
25 changed files with 542 additions and 306 deletions
+4 -4
View File
@@ -17,22 +17,22 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
- name: Set up Go 1.25
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.25
id: go
- uses: actions/checkout@v1
- name: Run tests
run: |
go test ./... -v -race -coverprofile cover.out -covermode=atomic
- name: Convert coverage to lcov
uses: jandelgado/gcov2lcov-action@v1.0.0
uses: jandelgado/gcov2lcov-action@v1
with:
infile: cover.out
outfile: coverage.lcov
- name: Coveralls
uses: coverallsapp/github-action@v1.0.1
uses: coverallsapp/github-action@v1
with:
github-token: ${{ secrets.github_token }}
path-to-lcov: coverage.lcov
+1 -1
View File
@@ -124,7 +124,7 @@ func main() {
router := xmpp.NewRouter()
router.HandleFunc("message", handleMessage)
client, err := xmpp.NewClient(config, router, errorHandler)
client, err := xmpp.NewClient(&config, router, errorHandler)
if err != nil {
log.Fatalf("%+v", err)
}
+1 -1
View File
@@ -1,6 +1,6 @@
module gosrc.io/xmpp/_examples
go 1.13
go 1.25
require (
github.com/processone/mpg123 v1.0.0
+4
View File
@@ -53,10 +53,12 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -189,6 +191,7 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -212,3 +215,4 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
+11 -6
View File
@@ -42,12 +42,12 @@ func (scs *SyncConnState) setState(cs ConnState) {
// This is a the list of events happening on the connection that the
// client can be notified about.
const (
InitialPresence = "<presence/>"
StateDisconnected ConnState = iota
StateResuming
StateSessionEstablished
StateStreamError
StatePermanentError
InitialPresence = "<presence/>"
)
// Event is a structure use to convey event changes related to client state. This
@@ -285,7 +285,7 @@ func (c *Client) Resume() error {
if err != nil {
return err
}
// Execute post reconnect hook. This can be different from the first connection hook, and not trigger roster retrival
// Execute post reconnect hook. This can be different from the first connection hook, and not trigger roster retrieval
// for example.
if c.PostResumeHook != nil {
err = c.PostResumeHook()
@@ -296,7 +296,11 @@ func (c *Client) Resume() error {
// Disconnect disconnects the client from the server, sending a stream close nonza and closing the TCP connection.
func (c *Client) Disconnect() error {
if c.transport != nil {
return c.transport.Close()
err := c.transport.Close()
if c.Session != nil {
c.disconnected(c.Session.SMState)
}
return err
}
// No transport so no connection.
return nil
@@ -379,11 +383,12 @@ func (c *Client) sendWithWriter(writer io.Writer, packet []byte) error {
// Loop: Receive data from server
func (c *Client) recv(keepaliveQuit chan<- struct{}) {
defer close(keepaliveQuit)
for {
val, err := stanza.NextPacket(c.transport.GetDecoder())
if err != nil {
c.ErrorHandler(err)
close(keepaliveQuit)
c.disconnected(c.Session.SMState)
return
}
@@ -392,7 +397,6 @@ func (c *Client) recv(keepaliveQuit chan<- struct{}) {
switch packet := val.(type) {
case stanza.StreamError:
c.router.route(c, val)
close(keepaliveQuit)
c.streamError(packet.Error.Local, packet.Text)
c.ErrorHandler(errors.New("stream error: " + packet.Error.Local))
// We don't return here, because we want to wait for the stream close tag from the server, or timeout.
@@ -411,7 +415,8 @@ func (c *Client) recv(keepaliveQuit chan<- struct{}) {
case stanza.StreamClosePacket:
// TCP messages should arrive in order, so we can expect to get nothing more after this occurs
c.transport.ReceivedStreamClose()
return
c.Disconnect()
continue
default:
c.Session.SMState.Inbound++
}
+1 -1
View File
@@ -189,7 +189,7 @@ func TestClient_SendIQ(t *testing.T) {
client.ErrorHandler = errorHandler
res, err := client.SendIQ(ctx, iqReq)
if err != nil {
t.Errorf(err.Error())
t.Error(err)
}
select {
+1 -1
View File
@@ -1,6 +1,6 @@
module gosrc.io/xmpp/cmd
go 1.13
go 1.25
require (
github.com/bdlm/log v0.1.19
+4
View File
@@ -63,10 +63,12 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -226,6 +228,7 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -253,3 +256,4 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
+2 -2
View File
@@ -185,7 +185,7 @@ func TestSendIq(t *testing.T) {
select {
case <-res:
case err := <-errChan:
t.Fatalf(err.Error())
t.Fatal(err)
}
clientDone <- struct{}{}
}()
@@ -367,7 +367,7 @@ func TestStreamManagerDisconnect(t *testing.T) {
//=============================================================================
// Basic XMPP Server Mock Handlers.
//===============================
// ===============================
// Init mock server and connection
// Creating a mock server and connecting a Component to it. Initialized with given port and handler function
// The Component and mock are both returned
+5 -5
View File
@@ -1,10 +1,10 @@
module gosrc.io/xmpp
go 1.13
go 1.25
require (
github.com/google/go-cmp v0.3.1
github.com/google/uuid v1.1.1
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
nhooyr.io/websocket v1.6.5
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da
nhooyr.io/websocket v1.8.17
)
+8 -210
View File
@@ -1,210 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/awesome-gocui/gocui v0.6.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA=
github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
+11 -7
View File
@@ -23,7 +23,7 @@ const (
type Command struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/commands command"`
CommandElement CommandElement
CommandElements []CommandElement
BadAction *struct{} `xml:"bad-action,omitempty"`
BadLocale *struct{} `xml:"bad-locale,omitempty"`
@@ -56,6 +56,8 @@ type CommandElement interface {
}
type Actions struct {
XMLName xml.Name `xml:"actions"`
Prev *struct{} `xml:"prev,omitempty"`
Next *struct{} `xml:"next,omitempty"`
Complete *struct{} `xml:"complete,omitempty"`
@@ -68,6 +70,8 @@ func (a *Actions) Ref() string {
}
type Note struct {
XMLName xml.Name `xml:"note"`
Text string `xml:",cdata"`
Type string `xml:"type,attr,omitempty"`
}
@@ -117,22 +121,22 @@ func (c *Command) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var err error
switch tt.Name.Local {
case "affiliations":
case "actions":
a := Actions{}
err = d.DecodeElement(&a, &tt)
c.CommandElement = &a
case "configure":
c.CommandElements = append(c.CommandElements, &a)
case "note":
nt := Note{}
err = d.DecodeElement(&nt, &tt)
c.CommandElement = &nt
c.CommandElements = append(c.CommandElements, &nt)
case "x":
f := Form{}
err = d.DecodeElement(&f, &tt)
c.CommandElement = &f
c.CommandElements = append(c.CommandElements, &f)
default:
n := Node{}
err = d.DecodeElement(&n, &tt)
c.CommandElement = &n
c.CommandElements = append(c.CommandElements, &n)
if err != nil {
return err
}
+3 -2
View File
@@ -2,8 +2,9 @@ package stanza_test
import (
"encoding/xml"
"gosrc.io/xmpp/stanza"
"testing"
"gosrc.io/xmpp/stanza"
)
func TestMarshalCommands(t *testing.T) {
@@ -35,6 +36,6 @@ func TestMarshalCommands(t *testing.T) {
}
if err := compareMarshal(input, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
+70
View File
@@ -0,0 +1,70 @@
package stanza
import (
"errors"
"strings"
"time"
)
// Helper structures and functions to manage dates and timestamps as defined in
// XEP-0082: XMPP Date and Time Profiles (https://xmpp.org/extensions/xep-0082.html)
const dateLayoutXEP0082 = "2006-01-02"
const timeLayoutXEP0082 = "15:04:05+00:00"
var InvalidDateInput = errors.New("could not parse date. Input might not be in a supported format")
var InvalidDateOutput = errors.New("could not format date as desired")
type JabberDate struct {
value time.Time
}
func (d JabberDate) DateToString() string {
return d.value.Format(dateLayoutXEP0082)
}
func (d JabberDate) DateTimeToString(nanos bool) string {
if nanos {
return d.value.Format(time.RFC3339Nano)
}
return d.value.Format(time.RFC3339)
}
func (d JabberDate) TimeToString(nanos bool) (string, error) {
if nanos {
spl := strings.Split(d.value.Format(time.RFC3339Nano), "T")
if len(spl) != 2 {
return "", InvalidDateOutput
}
return spl[1], nil
}
spl := strings.Split(d.value.Format(time.RFC3339), "T")
if len(spl) != 2 {
return "", InvalidDateOutput
}
return spl[1], nil
}
func NewJabberDateFromString(strDate string) (JabberDate, error) {
t, err := time.Parse(time.RFC3339, strDate)
if err == nil {
return JabberDate{value: t}, nil
}
t, err = time.Parse(time.RFC3339Nano, strDate)
if err == nil {
return JabberDate{value: t}, nil
}
t, err = time.Parse(dateLayoutXEP0082, strDate)
if err == nil {
return JabberDate{value: t}, nil
}
t, err = time.Parse(timeLayoutXEP0082, strDate)
if err == nil {
return JabberDate{value: t}, nil
}
return JabberDate{}, InvalidDateInput
}
+191
View File
@@ -0,0 +1,191 @@
package stanza
import (
"testing"
"time"
)
func TestDateToString(t *testing.T) {
t1 := JabberDate{value: time.Now()}
t2 := JabberDate{value: time.Now().Add(24 * time.Hour)}
t1Str := t1.DateToString()
t2Str := t2.DateToString()
if t1Str == t2Str {
t.Fatalf("time representations should not be identical")
}
}
func TestDateToStringOracle(t *testing.T) {
expected := "2009-11-10"
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
t1Str := t1.DateToString()
if t1Str != expected {
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
}
}
func TestDateTimeToString(t *testing.T) {
t1 := JabberDate{value: time.Now()}
t2 := JabberDate{value: time.Now().Add(10 * time.Second)}
t1Str := t1.DateTimeToString(false)
t2Str := t2.DateTimeToString(false)
if t1Str == t2Str {
t.Fatalf("time representations should not be identical")
}
}
func TestDateTimeToStringOracle(t *testing.T) {
expected := "2009-11-10T23:03:22+08:00"
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
t1Str := t1.DateTimeToString(false)
if t1Str != expected {
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
}
}
func TestDateTimeToStringNanos(t *testing.T) {
t1 := JabberDate{value: time.Now()}
time.After(10 * time.Millisecond)
t2 := JabberDate{value: time.Now()}
t1Str := t1.DateTimeToString(true)
t2Str := t2.DateTimeToString(true)
if t1Str == t2Str {
t.Fatalf("time representations should not be identical")
}
}
func TestDateTimeToStringNanosOracle(t *testing.T) {
expected := "2009-11-10T23:03:22.000000089+08:00"
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
t1Str := t1.DateTimeToString(true)
if t1Str != expected {
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
}
}
func TestTimeToString(t *testing.T) {
t1 := JabberDate{value: time.Now()}
t2 := JabberDate{value: time.Now().Add(10 * time.Second)}
t1Str, err := t1.TimeToString(false)
if err != nil {
t.Fatal(err)
}
t2Str, err := t2.TimeToString(false)
if err != nil {
t.Fatal(err)
}
if t1Str == t2Str {
t.Fatalf("time representations should not be identical")
}
}
func TestTimeToStringOracle(t *testing.T) {
expected := "23:03:22+08:00"
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
t1Str, err := t1.TimeToString(false)
if err != nil {
t.Fatal(err)
}
if t1Str != expected {
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
}
}
func TestTimeToStringNanos(t *testing.T) {
t1 := JabberDate{value: time.Now()}
time.Sleep(10 * time.Millisecond)
t2 := JabberDate{value: time.Now()}
t1Str, err := t1.TimeToString(true)
if err != nil {
t.Fatal(err)
}
t2Str, err := t2.TimeToString(true)
if err != nil {
t.Fatal(err)
}
if t1Str == t2Str {
t.Fatalf("time representations should not be identical")
}
}
func TestTimeToStringNanosOracle(t *testing.T) {
expected := "23:03:22.000000089+08:00"
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
t1Str, err := t1.TimeToString(true)
if err != nil {
t.Fatal(err)
}
if t1Str != expected {
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
}
}
func TestJabberDateParsing(t *testing.T) {
date := "2009-11-10"
_, err := NewJabberDateFromString(date)
if err != nil {
t.Fatal(err)
}
dateTime := "2009-11-10T23:03:22+08:00"
_, err = NewJabberDateFromString(dateTime)
if err != nil {
t.Fatal(err)
}
dateTimeNanos := "2009-11-10T23:03:22.000000089+08:00"
_, err = NewJabberDateFromString(dateTimeNanos)
if err != nil {
t.Fatal(err)
}
// TODO : fix these. Parsing a time with an offset doesn't work
//time := "23:03:22+08:00"
//_, err = NewJabberDateFromString(time)
//if err != nil {
// t.Fatalf(err.Error())
//}
//timeNanos := "23:03:22.000000089+08:00"
//_, err = NewJabberDateFromString(timeNanos)
//if err != nil {
// t.Fatalf(err.Error())
//}
}
+12 -2
View File
@@ -51,11 +51,21 @@ func NewJid(sjid string) (*Jid, error) {
}
func (j *Jid) Full() string {
return j.Node + "@" + j.Domain + "/" + j.Resource
if j.Resource == "" {
return j.Bare()
} else if j.Node == "" {
return j.Node + "/" + j.Resource
} else {
return j.Node + "@" + j.Domain + "/" + j.Resource
}
}
func (j *Jid) Bare() string {
return j.Node + "@" + j.Domain
if j.Node == "" {
return j.Domain
} else {
return j.Node + "@" + j.Domain
}
}
// ============================================================================
+30 -15
View File
@@ -61,26 +61,41 @@ func TestIncorrectJids(t *testing.T) {
}
func TestFull(t *testing.T) {
jid := "test@domain.com/my resource"
parsedJid, err := NewJid(jid)
if err != nil {
t.Errorf("could not parse jid: %v", err)
fullJids := []string{
"test@domain.com/my resource",
"test@domain.com",
"domain.com",
}
fullJid := parsedJid.Full()
if fullJid != jid {
t.Errorf("incorrect full jid: %s", fullJid)
for _, sjid := range fullJids {
parsedJid, err := NewJid(sjid)
if err != nil {
t.Errorf("could not parse jid: %v", err)
}
fullJid := parsedJid.Full()
if fullJid != sjid {
t.Errorf("incorrect full jid: %s", fullJid)
}
}
}
func TestBare(t *testing.T) {
jid := "test@domain.com"
fullJid := jid + "/my resource"
parsedJid, err := NewJid(fullJid)
if err != nil {
t.Errorf("could not parse jid: %v", err)
tests := []struct {
jidstr string
expected string
}{
{jidstr: "test@domain.com", expected: "test@domain.com"},
{jidstr: "test@domain.com/resource", expected: "test@domain.com"},
{jidstr: "domain.com", expected: "domain.com"},
}
bareJid := parsedJid.Bare()
if bareJid != jid {
t.Errorf("incorrect bare jid: %s", bareJid)
for _, tt := range tests {
parsedJid, err := NewJid(tt.jidstr)
if err != nil {
t.Errorf("could not parse jid: %v", err)
}
bareJid := parsedJid.Bare()
if bareJid != tt.expected {
t.Errorf("incorrect bare jid: %s", bareJid)
}
}
}
+36
View File
@@ -0,0 +1,36 @@
package stanza
import "encoding/xml"
/*
Support for:
- XEP-0334: Message Processing Hints: https://xmpp.org/extensions/xep-0334.html
Pointers should be used to keep consistent with unmarshal. Eg :
msg.Extensions = append(msg.Extensions, &stanza.HintNoCopy{}, &stanza.HintStore{})
*/
type HintNoPermanentStore struct {
MsgExtension
XMLName xml.Name `xml:"urn:xmpp:hints no-permanent-store"`
}
type HintNoStore struct {
MsgExtension
XMLName xml.Name `xml:"urn:xmpp:hints no-store"`
}
type HintNoCopy struct {
MsgExtension
XMLName xml.Name `xml:"urn:xmpp:hints no-copy"`
}
type HintStore struct {
MsgExtension
XMLName xml.Name `xml:"urn:xmpp:hints store"`
}
func init() {
TypeRegistry.MapExtension(PKTMessage, xml.Name{Space: "urn:xmpp:hints", Local: "no-permanent-store"}, HintNoPermanentStore{})
TypeRegistry.MapExtension(PKTMessage, xml.Name{Space: "urn:xmpp:hints", Local: "no-store"}, HintNoStore{})
TypeRegistry.MapExtension(PKTMessage, xml.Name{Space: "urn:xmpp:hints", Local: "no-copy"}, HintNoCopy{})
TypeRegistry.MapExtension(PKTMessage, xml.Name{Space: "urn:xmpp:hints", Local: "store"}, HintStore{})
}
+72
View File
@@ -0,0 +1,72 @@
package stanza_test
import (
"encoding/xml"
"gosrc.io/xmpp/stanza"
"reflect"
"strings"
"testing"
)
const msg_const = `
<message
from="romeo@montague.lit/laptop"
to="juliet@capulet.lit/laptop">
<body>V unir avtugf pybnx gb uvqr zr sebz gurve fvtug</body>
<no-copy xmlns="urn:xmpp:hints"></no-copy>
<no-permanent-store xmlns="urn:xmpp:hints"></no-permanent-store>
<no-store xmlns="urn:xmpp:hints"></no-store>
<store xmlns="urn:xmpp:hints"></store>
</message>`
func TestSerializationHint(t *testing.T) {
msg := stanza.NewMessage(stanza.Attrs{To: "juliet@capulet.lit/laptop", From: "romeo@montague.lit/laptop"})
msg.Body = "V unir avtugf pybnx gb uvqr zr sebz gurve fvtug"
msg.Extensions = append(msg.Extensions, stanza.HintNoCopy{}, stanza.HintNoPermanentStore{}, stanza.HintNoStore{}, stanza.HintStore{})
data, _ := xml.Marshal(msg)
if strings.ReplaceAll(strings.Join(strings.Fields(msg_const), ""), "\n", "") != strings.Join(strings.Fields(string(data)), "") {
t.Fatalf("marshalled message does not match expected message")
}
}
func TestUnmarshalHints(t *testing.T) {
// Init message as in the const value
msgConst := stanza.NewMessage(stanza.Attrs{To: "juliet@capulet.lit/laptop", From: "romeo@montague.lit/laptop"})
msgConst.Body = "V unir avtugf pybnx gb uvqr zr sebz gurve fvtug"
msgConst.Extensions = append(msgConst.Extensions, &stanza.HintNoCopy{}, &stanza.HintNoPermanentStore{}, &stanza.HintNoStore{}, &stanza.HintStore{})
// Compare message with the const value
msg := stanza.Message{}
err := xml.Unmarshal([]byte(msg_const), &msg)
if err != nil {
t.Fatal(err)
}
if msgConst.XMLName.Local != msg.XMLName.Local {
t.Fatalf("message tags do not match. Expected: %s, Actual: %s", msgConst.XMLName.Local, msg.XMLName.Local)
}
if msgConst.Body != msg.Body {
t.Fatalf("message bodies do not match. Expected: %s, Actual: %s", msgConst.Body, msg.Body)
}
if !reflect.DeepEqual(msgConst.Attrs, msg.Attrs) {
t.Fatalf("attributes do not match")
}
if !reflect.DeepEqual(msgConst.Error, msg.Error) {
t.Fatalf("attributes do not match")
}
var found bool
for _, ext := range msgConst.Extensions {
for _, strExt := range msg.Extensions {
if reflect.TypeOf(ext) == reflect.TypeOf(strExt) {
found = true
break
}
}
if !found {
t.Fatalf("extensions do not match")
}
found = false
}
}
+14 -7
View File
@@ -237,10 +237,10 @@ func NewApprovePendingSubRequest(serviceId, sessionId, nodeId string) (*IQ, erro
}
iq.Payload = &Command{
// the command name ('node' attribute of the command element) MUST have a value of "http://jabber.org/protocol/pubsub#get-pending"
Node: "http://jabber.org/protocol/pubsub#get-pending",
Action: CommandActionExecute,
SessionId: sessionId,
CommandElement: &n,
Node: "http://jabber.org/protocol/pubsub#get-pending",
Action: CommandActionExecute,
SessionId: sessionId,
CommandElements: []CommandElement{&n},
}
return iq, nil
}
@@ -353,11 +353,18 @@ func (iq *IQ) GetFormFields() (map[string]*Field, error) {
case *Command:
fieldMap := make(map[string]*Field)
co, ok := payload.CommandElement.(*Form)
if !ok {
var form *Form
for _, ce := range payload.CommandElements {
fo, ok := ce.(*Form)
if ok {
form = fo
break
}
}
if form == nil {
return nil, errors.New("this IQ does not contain a command payload with a form")
}
for _, elt := range co.Fields {
for _, elt := range form.Fields {
fieldMap[elt.Var] = elt
}
return fieldMap, nil
+16 -15
View File
@@ -3,8 +3,9 @@ package stanza_test
import (
"encoding/xml"
"errors"
"gosrc.io/xmpp/stanza"
"testing"
"gosrc.io/xmpp/stanza"
)
// ******************************
@@ -45,7 +46,7 @@ func TestNewConfigureNode(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -100,7 +101,7 @@ func TestNewConfigureNodeResp(t *testing.T) {
pubsub, err := getPubSubOwnerPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.OwnerUseCase == nil {
t.Fatalf("owner use case is nil")
@@ -154,7 +155,7 @@ func TestNewRequestDefaultConfig(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -209,7 +210,7 @@ func TestNewRequestDefaultConfigResp(t *testing.T) {
pubsub, err := getPubSubOwnerPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.OwnerUseCase == nil {
t.Fatalf("owner use case is nil")
@@ -264,7 +265,7 @@ func TestNewDelNode(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -281,7 +282,7 @@ func TestNewDelNodeResp(t *testing.T) {
pubsub, err := getPubSubOwnerPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.OwnerUseCase == nil {
t.Fatalf("owner use case is nil")
@@ -340,7 +341,7 @@ func TestNewPurgeAllItems(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -390,7 +391,7 @@ func TestNewApproveSubRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -428,7 +429,7 @@ func TestNewGetPendingSubRequests(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -510,7 +511,7 @@ func TestNewApprovePendingSubRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -549,7 +550,7 @@ func TestNewSubListRqPl(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -624,7 +625,7 @@ func TestNewAffiliationListRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -718,7 +719,7 @@ func TestNewModifAffiliationRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -864,7 +865,7 @@ func TestNewFormSubmissionOwner(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
+19 -18
View File
@@ -3,9 +3,10 @@ package stanza_test
import (
"encoding/xml"
"errors"
"gosrc.io/xmpp/stanza"
"strings"
"testing"
"gosrc.io/xmpp/stanza"
)
var submitFormExample = stanza.NewForm([]*stanza.Field{
@@ -51,7 +52,7 @@ func TestNewSubRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -68,7 +69,7 @@ func TestNewSubResp(t *testing.T) {
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.Subscription == nil {
@@ -114,7 +115,7 @@ func TestNewUnSubRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -130,7 +131,7 @@ func TestNewUnsubResp(t *testing.T) {
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.Subscription == nil {
@@ -175,7 +176,7 @@ func TestNewSubOptsRq(t *testing.T) {
}
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -228,7 +229,7 @@ func TestNewNewConfOptsRsp(t *testing.T) {
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.SubOptions == nil {
@@ -286,7 +287,7 @@ func TestNewFormSubmission(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -343,7 +344,7 @@ func TestNewSubAndConfig(t *testing.T) {
}
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -381,7 +382,7 @@ func TestNewSubAndConfigResp(t *testing.T) {
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.Subscription == nil {
t.Fatalf("sub node is nil")
@@ -459,7 +460,7 @@ func TestNewItemsResp(t *testing.T) {
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.Items == nil {
t.Fatalf("sub options node is nil")
@@ -500,7 +501,7 @@ func TestNewSpecificItemRequest(t *testing.T) {
}
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -664,7 +665,7 @@ func TestNewDelItemFromNode(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -700,7 +701,7 @@ func TestNewCreateNode(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -714,7 +715,7 @@ func TestNewCreateNodeResp(t *testing.T) {
`
pubsub, err := getPubSubGenericPayload(response)
if err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
if pubsub.Create == nil {
t.Fatalf("create segment is nil")
@@ -783,7 +784,7 @@ func TestNewCreateAndConfigNode(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expectedReq, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -808,7 +809,7 @@ func TestNewRetrieveAllSubsRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expected, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
@@ -868,7 +869,7 @@ func TestNewRetrieveAllAffilsRequest(t *testing.T) {
data, err := xml.Marshal(subR)
if err := compareMarshal(expected, string(data)); err != nil {
t.Fatalf(err.Error())
t.Fatal(err)
}
}
+2 -2
View File
@@ -24,6 +24,6 @@ type ResultSet struct {
type First struct {
XMLName xml.Name `xml:"first"`
Content string
Index *int `xml:"index,attr,omitempty"`
Content string `xml:",chardata"`
Index *int `xml:"index,attr,omitempty"`
}
+19 -6
View File
@@ -80,13 +80,13 @@ func (sm *StreamManager) Run() error {
sm.Metrics.setLoginTime()
case StateDisconnected:
// Reconnect on disconnection
return sm.resume(e.SMState)
return sm.resume()
case StateStreamError:
sm.client.Disconnect()
// Only try reconnecting if we have not been kicked by another session to avoid connection loop.
// TODO: Make this conflict exception a permanent error
if e.StreamError != "conflict" {
return sm.connect()
return sm.resume()
}
case StatePermanentError:
// Do not attempt to reconnect
@@ -113,19 +113,32 @@ func (sm *StreamManager) Stop() {
}
func (sm *StreamManager) connect() error {
var state SMState
return sm.resume(state)
if sm.client != nil {
if c, ok := sm.client.(*Client); ok {
if c.CurrentState.getState() == StateDisconnected {
sm.Metrics = initMetrics()
err := c.Connect()
if err != nil {
return err
}
if sm.PostConnect != nil {
sm.PostConnect(sm.client)
}
return nil
}
}
}
return errors.New("client is not disconnected")
}
// resume manages the reconnection loop and apply the define backoff to avoid overloading the server.
func (sm *StreamManager) resume(state SMState) error {
func (sm *StreamManager) resume() error {
var backoff backoff // TODO: Group backoff calculation features with connection manager?
for {
var err error
// TODO: Make it possible to define logger to log disconnect and reconnection attempts
sm.Metrics = initMetrics()
if err = sm.client.Resume(); err != nil {
var actualErr ConnError
if xerrors.As(err, &actualErr) {
+5 -1
View File
@@ -35,12 +35,15 @@ var clientStreamOpen = fmt.Sprintf("<?xml version='1.0'?><stream:stream to='%%s'
func (t *XMPPTransport) Connect() (string, error) {
var err error
// Since we're starting a new connection, reset the encryption status
t.isSecure = false
t.conn, err = net.DialTimeout("tcp", t.Config.Address, time.Duration(t.Config.ConnectTimeout)*time.Second)
if err != nil {
return "", NewConnError(err, true)
}
t.closeChan = make(chan stanza.StreamClosePacket)
t.closeChan = make(chan stanza.StreamClosePacket, 1)
t.readWriter = newStreamLogger(t.conn, t.logFile)
t.decoder = xml.NewDecoder(bufio.NewReaderSize(t.readWriter, maxPacketSize))
t.decoder.CharsetReader = t.Config.CharsetReader
@@ -93,6 +96,7 @@ func (t *XMPPTransport) StartTLS() error {
return err
}
t.isSecure = false
t.conn = tlsConn
t.readWriter = newStreamLogger(tlsConn, t.logFile)
t.decoder = xml.NewDecoder(bufio.NewReaderSize(t.readWriter, maxPacketSize))