Merge lp:~pedronis/ubuntu-push/unicast-preps into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Superseded
Proposed branch: lp:~pedronis/ubuntu-push/unicast-preps
Merge into: lp:ubuntu-push
Diff against target: 3632 lines (+1467/-388)
49 files modified
Makefile (+3/-0)
README (+12/-2)
bus/connectivity/connectivity.go (+8/-7)
bus/connectivity/connectivity_test.go (+77/-3)
client/client.go (+8/-9)
client/client_test.go (+15/-1)
client/gethosts/gethost.go (+7/-5)
client/gethosts/gethost_test.go (+6/-2)
client/session/session.go (+86/-16)
client/session/session_test.go (+232/-16)
config/config.go (+130/-21)
config/config_test.go (+105/-0)
debian/changelog (+6/-0)
debian/control (+4/-0)
debian/rules (+4/-1)
dependencies.tsv (+2/-0)
logger/logger.go (+27/-0)
logger/logger_test.go (+25/-0)
protocol/messages.go (+45/-0)
protocol/messages_test.go (+18/-2)
protocol/state-diag-client.gv (+4/-1)
protocol/state-diag-client.svg (+77/-49)
protocol/state-diag-session.gv (+10/-0)
protocol/state-diag-session.svg (+132/-74)
server/acceptance/acceptanceclient.go (+5/-0)
server/acceptance/cmd/acceptanceclient.go (+7/-2)
server/acceptance/suites/broadcast.go (+6/-2)
server/acceptance/suites/suite.go (+9/-0)
server/api/handlers_test.go (+3/-2)
server/broker/broker.go (+1/-1)
server/broker/exchanges.go (+31/-35)
server/broker/exchanges_test.go (+29/-25)
server/broker/exchg_impl_test.go (+19/-17)
server/broker/simple/simple.go (+10/-10)
server/broker/simple/simple_test.go (+5/-4)
server/broker/testsuite/suite.go (+36/-33)
server/session/session.go (+23/-11)
server/session/session_test.go (+43/-14)
server/session/tracker.go (+12/-5)
server/session/tracker_test.go (+4/-4)
server/store/inmemory.go (+8/-6)
server/store/inmemory_test.go (+6/-3)
server/store/store.go (+3/-1)
testing/helpers.go (+11/-0)
ubuntu-push-client.go (+29/-0)
util/auth.go (+36/-0)
util/auth_test.go (+53/-0)
whoopsie/identifier/identifier.go (+20/-4)
whoopsie/identifier/identifier_test.go (+15/-0)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/unicast-preps
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+217654@code.launchpad.net

Commit message

reorganize things such that GetChannelSnapshot returns a bunch of Notification (with optional AppId, MsgId) so that it can be used together with the underlying store to store unicast notification user/device channels

Description of the change

reorganize things such that GetChannelSnapshot returns a bunch of Notification (with optional AppId, MsgId) so that it can be used together with the underlying store to store unicast notification user/device channels

To post a comment you must log in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2014-03-31 17:58:54 +0000
+++ Makefile 2014-04-29 18:02:00 +0000
@@ -11,10 +11,13 @@
11GODEPS += launchpad.net/go-dbus/v111GODEPS += launchpad.net/go-dbus/v1
12GODEPS += launchpad.net/go-xdg/v012GODEPS += launchpad.net/go-xdg/v0
13GODEPS += code.google.com/p/gosqlite/sqlite313GODEPS += code.google.com/p/gosqlite/sqlite3
14GODEPS += gopkg.in/qml.v0
15GODEPS += gopkg.in/niemeyer/uoneauth.v1
1416
15TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client )17TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client )
1618
17bootstrap:19bootstrap:
20 $(RM) -r $(GOPATH)/pkg
18 mkdir -p $(GOPATH)/bin21 mkdir -p $(GOPATH)/bin
19 mkdir -p $(GOPATH)/pkg22 mkdir -p $(GOPATH)/pkg
20 go get -u launchpad.net/godeps23 go get -u launchpad.net/godeps
2124
=== modified file 'README'
--- README 2014-03-31 17:58:54 +0000
+++ README 2014-04-29 18:02:00 +0000
@@ -6,11 +6,21 @@
6The code expects to be checked out as launchpad.net/ubuntu-push in a Go6The code expects to be checked out as launchpad.net/ubuntu-push in a Go
7workspace, see "go help gopath".7workspace, see "go help gopath".
88
9To setup Go dependencies, install libsqlite3-dev and run:9To setup Go dependencies, install the following dependencies:
10
11 build-essential
12 libsqlite3-dev
13 qtbase5-private-dev
14 qtdeclarative5-dev
15 libqt5opengl5-dev
16 libubuntuoneauth-2.0-dev
17
18and run:
1019
11 make bootstrap20 make bootstrap
1221
13To run tests, install libgcrypt11-dev and libwhoopsie-dev and run:22To run tests, install libglib2.0-dev, libgcrypt11-dev, libwhoopsie-dev,
23and run:
1424
15 make check25 make check
1626
1727
=== modified file 'bus/connectivity/connectivity.go'
--- bus/connectivity/connectivity.go 2014-04-04 11:08:28 +0000
+++ bus/connectivity/connectivity.go 2014-04-29 18:02:00 +0000
@@ -72,19 +72,20 @@
72 cs.connAttempts += ar.Redial()72 cs.connAttempts += ar.Redial()
73 nm := networkmanager.New(cs.endp, cs.log)73 nm := networkmanager.New(cs.endp, cs.log)
7474
75 // set up the watch
76 stateCh, err = nm.WatchState()
77 if err != nil {
78 cs.log.Debugf("failed to set up the state watch: %s", err)
79 goto Continue
80 }
81
75 // Get the current state.82 // Get the current state.
76 initial = nm.GetState()83 initial = nm.GetState()
77 if initial == networkmanager.Unknown {84 if initial == networkmanager.Unknown {
78 cs.log.Debugf("Failed to get state.")85 cs.log.Debugf("Failed to get state.")
79 goto Continue86 goto Continue
80 }87 }
8188 cs.log.Debugf("got initial state of %s", initial)
82 // set up the watch
83 stateCh, err = nm.WatchState()
84 if err != nil {
85 cs.log.Debugf("failed to set up the state watch: %s", err)
86 goto Continue
87 }
8889
89 primary = nm.GetPrimaryConnection()90 primary = nm.GetPrimaryConnection()
90 cs.log.Debugf("primary connection starts as %#v", primary)91 cs.log.Debugf("primary connection starts as %#v", primary)
9192
=== modified file 'bus/connectivity/connectivity_test.go'
--- bus/connectivity/connectivity_test.go 2014-04-04 12:01:42 +0000
+++ bus/connectivity/connectivity_test.go 2014-04-29 18:02:00 +0000
@@ -17,8 +17,15 @@
17package connectivity17package connectivity
1818
19import (19import (
20 "net/http/httptest"
21 "sync"
22 "testing"
23 "time"
24
20 "launchpad.net/go-dbus/v1"25 "launchpad.net/go-dbus/v1"
21 . "launchpad.net/gocheck"26 . "launchpad.net/gocheck"
27
28 "launchpad.net/ubuntu-push/bus"
22 "launchpad.net/ubuntu-push/bus/networkmanager"29 "launchpad.net/ubuntu-push/bus/networkmanager"
23 testingbus "launchpad.net/ubuntu-push/bus/testing"30 testingbus "launchpad.net/ubuntu-push/bus/testing"
24 "launchpad.net/ubuntu-push/config"31 "launchpad.net/ubuntu-push/config"
@@ -26,9 +33,6 @@
26 helpers "launchpad.net/ubuntu-push/testing"33 helpers "launchpad.net/ubuntu-push/testing"
27 "launchpad.net/ubuntu-push/testing/condition"34 "launchpad.net/ubuntu-push/testing/condition"
28 "launchpad.net/ubuntu-push/util"35 "launchpad.net/ubuntu-push/util"
29 "net/http/httptest"
30 "testing"
31 "time"
32)36)
3337
34// hook up gocheck38// hook up gocheck
@@ -115,6 +119,76 @@
115 c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal)119 c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal)
116}120}
117121
122// a racyEndpoint is an endpoint that behaves differently depending on
123// how much time passes between getting the state and setting up the
124// watch
125type racyEndpoint struct {
126 stateGot bool
127 maxTime time.Time
128 delta time.Duration
129 lock sync.RWMutex
130}
131
132func (rep *racyEndpoint) GetProperty(prop string) (interface{}, error) {
133 switch prop {
134 case "state":
135 rep.lock.Lock()
136 defer rep.lock.Unlock()
137 rep.stateGot = true
138 rep.maxTime = time.Now().Add(rep.delta)
139 return uint32(networkmanager.Connecting), nil
140 case "PrimaryConnection":
141 return dbus.ObjectPath("/something"), nil
142 default:
143 return nil, nil
144 }
145}
146
147func (rep *racyEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error {
148 if member == "StateChanged" {
149 // we count never having gotten the state as happening "after" now.
150 rep.lock.RLock()
151 defer rep.lock.RUnlock()
152 ok := !rep.stateGot || time.Now().Before(rep.maxTime)
153 go func() {
154 if ok {
155 f(uint32(networkmanager.ConnectedGlobal))
156 }
157 d()
158 }()
159 }
160 return nil
161}
162
163func (*racyEndpoint) Close() {}
164func (*racyEndpoint) Dial() error { return nil }
165func (*racyEndpoint) String() string { return "racyEndpoint" }
166func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil }
167
168var _ bus.Endpoint = (*racyEndpoint)(nil)
169
170// takeNext takes a value from given channel with a 1s timeout
171func takeNext(ch <-chan networkmanager.State) networkmanager.State {
172 select {
173 case <-time.After(time.Second):
174 panic("channel stuck: too long waiting")
175 case v := <-ch:
176 return v
177 }
178}
179
180// test that if the nm state goes from connecting to connected very
181// shortly after calling GetState, we don't lose the event.
182func (s *ConnSuite) TestStartAvoidsRace(c *C) {
183 for delta := time.Second; delta > 1; delta /= 2 {
184 rep := &racyEndpoint{delta: delta}
185 cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: rep}
186 f := Commentf("when delta=%s", delta)
187 c.Assert(cs.start(), Equals, networkmanager.Connecting, f)
188 c.Assert(takeNext(cs.networkStateCh), Equals, networkmanager.ConnectedGlobal, f)
189 }
190}
191
118/*192/*
119 tests for connectedStateStep()193 tests for connectedStateStep()
120*/194*/
121195
=== modified file 'client/client.go'
--- client/client.go 2014-04-11 16:37:48 +0000
+++ client/client.go 2014-04-29 18:02:00 +0000
@@ -26,6 +26,7 @@
26 "os"26 "os"
27 "strings"27 "strings"
2828
29 "gopkg.in/qml.v0"
29 "launchpad.net/go-dbus/v1"30 "launchpad.net/go-dbus/v1"
3031
31 "launchpad.net/ubuntu-push/bus"32 "launchpad.net/ubuntu-push/bus"
@@ -57,7 +58,7 @@
57 // The PEM-encoded server certificate58 // The PEM-encoded server certificate
58 CertPEMFile string `json:"cert_pem_file"`59 CertPEMFile string `json:"cert_pem_file"`
59 // The logging level (one of "debug", "info", "error")60 // The logging level (one of "debug", "info", "error")
60 LogLevel string `json:"log_level"`61 LogLevel logger.ConfigLogLevel `json:"log_level"`
61}62}
6263
63// PushClient is the Ubuntu Push Notifications client-side daemon.64// PushClient is the Ubuntu Push Notifications client-side daemon.
@@ -95,13 +96,13 @@
9596
96// configure loads its configuration, and sets it up.97// configure loads its configuration, and sets it up.
97func (client *PushClient) configure() error {98func (client *PushClient) configure() error {
98 f, err := os.Open(client.configPath)99 _, err := os.Stat(client.configPath)
99 if err != nil {100 if err != nil {
100 return fmt.Errorf("opening config: %v", err)101 return fmt.Errorf("config: %v", err)
101 }102 }
102 err = config.ReadConfig(f, &client.config)103 err = config.ReadFiles(&client.config, client.configPath, "<flags>")
103 if err != nil {104 if err != nil {
104 return fmt.Errorf("reading config: %v", err)105 return fmt.Errorf("config: %v", err)
105 }106 }
106 // ignore spaces107 // ignore spaces
107 client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1)108 client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1)
@@ -110,7 +111,8 @@
110 }111 }
111112
112 // later, we'll be specifying more logging options in the config file113 // later, we'll be specifying more logging options in the config file
113 client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel)114 client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel.Level())
115 qml.SetLogger(client.log)
114116
115 // overridden for testing117 // overridden for testing
116 client.idder = identifier.New()118 client.idder = identifier.New()
@@ -285,9 +287,6 @@
285 h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}}287 h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}}
286 nots := notifications.Raw(client.notificationsEndp, client.log)288 nots := notifications.Raw(client.notificationsEndp, client.log)
287 body := "Tap to open the system updater."289 body := "Tap to open the system updater."
288 if msg != nil {
289 body = fmt.Sprintf("[%d] %s", msg.TopLevel, body)
290 }
291 not_id, err := nots.Notify(290 not_id, err := nots.Notify(
292 "ubuntu-push-client", // app name291 "ubuntu-push-client", // app name
293 uint32(0), // id292 uint32(0), // id
294293
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-04-11 16:21:45 +0000
+++ client/client_test.go 2014-04-29 18:02:00 +0000
@@ -19,10 +19,12 @@
19import (19import (
20 "encoding/json"20 "encoding/json"
21 "errors"21 "errors"
22 "flag"
22 "fmt"23 "fmt"
23 "io/ioutil"24 "io/ioutil"
24 "net/http"25 "net/http"
25 "net/http/httptest"26 "net/http/httptest"
27 "os"
26 "path/filepath"28 "path/filepath"
27 "reflect"29 "reflect"
28 "testing"30 "testing"
@@ -37,6 +39,7 @@
37 testibus "launchpad.net/ubuntu-push/bus/testing"39 testibus "launchpad.net/ubuntu-push/bus/testing"
38 "launchpad.net/ubuntu-push/client/session"40 "launchpad.net/ubuntu-push/client/session"
39 "launchpad.net/ubuntu-push/client/session/levelmap"41 "launchpad.net/ubuntu-push/client/session/levelmap"
42 "launchpad.net/ubuntu-push/config"
40 helpers "launchpad.net/ubuntu-push/testing"43 helpers "launchpad.net/ubuntu-push/testing"
41 "launchpad.net/ubuntu-push/testing/condition"44 "launchpad.net/ubuntu-push/testing/condition"
42 "launchpad.net/ubuntu-push/util"45 "launchpad.net/ubuntu-push/util"
@@ -79,6 +82,7 @@
79}82}
8083
81func (cs *clientSuite) SetUpSuite(c *C) {84func (cs *clientSuite) SetUpSuite(c *C) {
85 config.IgnoreParsedFlags = true // because configure() uses <flags>
82 cs.timeouts = util.SwapTimeouts([]time.Duration{0})86 cs.timeouts = util.SwapTimeouts([]time.Duration{0})
83 cs.leveldbPath = ""87 cs.leveldbPath = ""
84}88}
@@ -142,6 +146,16 @@
142 c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond))146 c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond))
143}147}
144148
149func (cs *clientSuite) TestConfigureWorksWithFlags(c *C) {
150 flag.CommandLine = flag.NewFlagSet("client", flag.ContinueOnError)
151 os.Args = []string{"client", "-addr", "foo:7777"}
152 cli := NewPushClient(cs.configPath, cs.leveldbPath)
153 err := cli.configure()
154 c.Assert(err, IsNil)
155 c.Assert(cli.config, NotNil)
156 c.Check(cli.config.Addr, Equals, "foo:7777")
157}
158
145func (cs *clientSuite) TestConfigureSetsUpLog(c *C) {159func (cs *clientSuite) TestConfigureSetsUpLog(c *C) {
146 cli := NewPushClient(cs.configPath, cs.leveldbPath)160 cli := NewPushClient(cs.configPath, cs.leveldbPath)
147 c.Check(cli.log, IsNil)161 c.Check(cli.log, IsNil)
@@ -163,7 +177,7 @@
163 c.Check(cli.idder, IsNil)177 c.Check(cli.idder, IsNil)
164 err := cli.configure()178 err := cli.configure()
165 c.Assert(err, IsNil)179 c.Assert(err, IsNil)
166 c.Assert(cli.idder, DeepEquals, identifier.New())180 c.Assert(cli.idder, FitsTypeOf, identifier.New())
167}181}
168182
169func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) {183func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) {
170184
=== modified file 'client/gethosts/gethost.go'
--- client/gethosts/gethost.go 2014-03-24 15:32:29 +0000
+++ client/gethosts/gethost.go 2014-04-29 18:02:00 +0000
@@ -49,8 +49,10 @@
49 }49 }
50}50}
5151
52type expected struct {52// Host contains the domain and hosts returned by the remote endpoint
53 Hosts []string53type Host struct {
54 Domain string
55 Hosts []string
54}56}
5557
56var (58var (
@@ -60,7 +62,7 @@
60)62)
6163
62// Get gets a list of hosts consulting the endpoint.64// Get gets a list of hosts consulting the endpoint.
63func (gh *GetHost) Get() ([]string, error) {65func (gh *GetHost) Get() (*Host, error) {
64 resp, err := gh.cli.Get(gh.endpointUrl + "?h=" + gh.hash)66 resp, err := gh.cli.Get(gh.endpointUrl + "?h=" + gh.hash)
65 if err != nil {67 if err != nil {
66 return nil, err68 return nil, err
@@ -80,7 +82,7 @@
80 if err != nil {82 if err != nil {
81 return nil, err83 return nil, err
82 }84 }
83 var parsed expected85 var parsed Host
84 err = json.Unmarshal(body, &parsed)86 err = json.Unmarshal(body, &parsed)
85 if err != nil {87 if err != nil {
86 return nil, ErrTemporary88 return nil, ErrTemporary
@@ -88,5 +90,5 @@
88 if len(parsed.Hosts) == 0 {90 if len(parsed.Hosts) == 0 {
89 return nil, ErrTemporary91 return nil, ErrTemporary
90 }92 }
91 return parsed.Hosts, nil93 return &parsed, nil
92}94}
9395
=== modified file 'client/gethosts/gethost_test.go'
--- client/gethosts/gethost_test.go 2014-03-31 14:31:07 +0000
+++ client/gethosts/gethost_test.go 2014-04-29 18:02:00 +0000
@@ -45,7 +45,8 @@
45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 x := r.FormValue("h")46 x := r.FormValue("h")
47 b, err := json.Marshal(map[string]interface{}{47 b, err := json.Marshal(map[string]interface{}{
48 "hosts": []string{"http://" + x},48 "domain": "example.com",
49 "hosts": []string{"http://" + x},
49 })50 })
50 if err != nil {51 if err != nil {
51 panic(err)52 panic(err)
@@ -57,7 +58,8 @@
57 gh := New("foobar", ts.URL, 1*time.Second)58 gh := New("foobar", ts.URL, 1*time.Second)
58 res, err := gh.Get()59 res, err := gh.Get()
59 c.Assert(err, IsNil)60 c.Assert(err, IsNil)
60 c.Check(res, DeepEquals, []string{"http://c1130408a700afe0"})61 c.Check(*res, DeepEquals,
62 Host{Domain: "example.com", Hosts: []string{"http://c1130408a700afe0"}})
61}63}
6264
63func (s *getHostsSuite) TestGetTimeout(c *C) {65func (s *getHostsSuite) TestGetTimeout(c *C) {
@@ -97,4 +99,6 @@
9799
98 scenario(http.StatusOK, "{", ErrTemporary)100 scenario(http.StatusOK, "{", ErrTemporary)
99 scenario(http.StatusOK, "{}", ErrTemporary)101 scenario(http.StatusOK, "{}", ErrTemporary)
102 scenario(http.StatusOK, `{"domain": "example.com"}`, ErrTemporary)
103 scenario(http.StatusOK, `{"hosts": ["one"]}`, nil)
100}104}
101105
=== modified file 'client/session/session.go'
--- client/session/session.go 2014-04-04 13:55:00 +0000
+++ client/session/session.go 2014-04-29 18:02:00 +0000
@@ -38,7 +38,11 @@
38 "launchpad.net/ubuntu-push/util"38 "launchpad.net/ubuntu-push/util"
39)39)
4040
41var wireVersionBytes = []byte{protocol.ProtocolWireVersion}41var (
42 wireVersionBytes = []byte{protocol.ProtocolWireVersion}
43 getAuthorization = util.GetAuthorization
44 shouldGetAuth = false
45)
4246
43type Notification struct {47type Notification struct {
44 TopLevel int6448 TopLevel int64
@@ -73,7 +77,7 @@
73)77)
7478
75type hostGetter interface {79type hostGetter interface {
76 Get() ([]string, error)80 Get() (*gethosts.Host, error)
77}81}
7882
79// ClientSessionConfig groups the client session configuration.83// ClientSessionConfig groups the client session configuration.
@@ -115,6 +119,28 @@
115 stateP *uint32119 stateP *uint32
116 ErrCh chan error120 ErrCh chan error
117 MsgCh chan *Notification121 MsgCh chan *Notification
122 // authorization
123 auth string
124 // autoredial knobs
125 shouldDelayP *uint32
126 lastAutoRedial time.Time
127 redialDelay func(*ClientSession) time.Duration
128 redialJitter func(time.Duration) time.Duration
129 redialDelays []time.Duration
130 redialDelaysIdx int
131}
132
133func redialDelay(sess *ClientSession) time.Duration {
134 if sess.ShouldDelay() {
135 t := sess.redialDelays[sess.redialDelaysIdx]
136 if len(sess.redialDelays) > sess.redialDelaysIdx+1 {
137 sess.redialDelaysIdx++
138 }
139 return t + sess.redialJitter(t)
140 } else {
141 sess.redialDelaysIdx = 0
142 return 0
143 }
118}144}
119145
120func NewSession(serverAddrSpec string, conf ClientSessionConfig,146func NewSession(serverAddrSpec string, conf ClientSessionConfig,
@@ -131,6 +157,7 @@
131 if hostsEndpoint != "" {157 if hostsEndpoint != "" {
132 getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout)158 getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout)
133 }159 }
160 var shouldDelay uint32 = 0
134 sess := &ClientSession{161 sess := &ClientSession{
135 ClientSessionConfig: conf,162 ClientSessionConfig: conf,
136 getHost: getHost,163 getHost: getHost,
@@ -139,10 +166,14 @@
139 Log: log,166 Log: log,
140 Protocolator: protocol.NewProtocol0,167 Protocolator: protocol.NewProtocol0,
141 Levels: levels,168 Levels: levels,
142 TLS: &tls.Config{InsecureSkipVerify: true}, // XXX169 TLS: &tls.Config{},
143 stateP: &state,170 stateP: &state,
144 timeSince: time.Since,171 timeSince: time.Since,
172 shouldDelayP: &shouldDelay,
173 redialDelay: redialDelay,
174 redialDelays: util.Timeouts(),
145 }175 }
176 sess.redialJitter = sess.Jitter
146 if sess.PEM != nil {177 if sess.PEM != nil {
147 cp := x509.NewCertPool()178 cp := x509.NewCertPool()
148 ok := cp.AppendCertsFromPEM(sess.PEM)179 ok := cp.AppendCertsFromPEM(sess.PEM)
@@ -154,6 +185,18 @@
154 return sess, nil185 return sess, nil
155}186}
156187
188func (sess *ClientSession) ShouldDelay() bool {
189 return atomic.LoadUint32(sess.shouldDelayP) != 0
190}
191
192func (sess *ClientSession) setShouldDelay() {
193 atomic.StoreUint32(sess.shouldDelayP, uint32(1))
194}
195
196func (sess *ClientSession) clearShouldDelay() {
197 atomic.StoreUint32(sess.shouldDelayP, uint32(0))
198}
199
157func (sess *ClientSession) State() ClientSessionState {200func (sess *ClientSession) State() ClientSessionState {
158 return ClientSessionState(atomic.LoadUint32(sess.stateP))201 return ClientSessionState(atomic.LoadUint32(sess.stateP))
159}202}
@@ -180,20 +223,38 @@
180 if sess.deliveryHosts != nil && sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime {223 if sess.deliveryHosts != nil && sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime {
181 return nil224 return nil
182 }225 }
183 hosts, err := sess.getHost.Get()226 host, err := sess.getHost.Get()
184 if err != nil {227 if err != nil {
185 sess.Log.Errorf("getHosts: %v", err)228 sess.Log.Errorf("getHosts: %v", err)
186 sess.setState(Error)229 sess.setState(Error)
187 return err230 return err
188 }231 }
189 sess.deliveryHostsTimestamp = time.Now()232 sess.deliveryHostsTimestamp = time.Now()
190 sess.deliveryHosts = hosts233 sess.deliveryHosts = host.Hosts
234 if sess.TLS != nil {
235 sess.TLS.ServerName = host.Domain
236 }
191 } else {237 } else {
192 sess.deliveryHosts = sess.fallbackHosts238 sess.deliveryHosts = sess.fallbackHosts
193 }239 }
194 return nil240 return nil
195}241}
196242
243// checkAuthorization checks the authorization within the phone
244func (sess *ClientSession) checkAuthorization() error {
245 // grab the authorization string from the accounts
246 // TODO: remove this condition when we have a way to deal with failing authorizations
247 if shouldGetAuth {
248 auth, err := getAuthorization()
249 if err != nil {
250 // For now we just log the error, as we don't want to block unauthorized users
251 sess.Log.Errorf("unable to get the authorization token from the account: %v", err)
252 }
253 sess.auth = auth
254 }
255 return nil
256}
257
197func (sess *ClientSession) resetHosts() {258func (sess *ClientSession) resetHosts() {
198 sess.deliveryHosts = nil259 sess.deliveryHosts = nil
199}260}
@@ -234,6 +295,7 @@
234// connect to a server using the configuration in the ClientSession295// connect to a server using the configuration in the ClientSession
235// and set up the connection.296// and set up the connection.
236func (sess *ClientSession) connect() error {297func (sess *ClientSession) connect() error {
298 sess.setShouldDelay()
237 sess.startConnectionAttempt()299 sess.startConnectionAttempt()
238 var err error300 var err error
239 var conn net.Conn301 var conn net.Conn
@@ -263,7 +325,12 @@
263325
264func (sess *ClientSession) AutoRedial(doneCh chan uint32) {326func (sess *ClientSession) AutoRedial(doneCh chan uint32) {
265 sess.stopRedial()327 sess.stopRedial()
328 if time.Since(sess.lastAutoRedial) < 2*time.Second {
329 sess.setShouldDelay()
330 }
331 time.Sleep(sess.redialDelay(sess))
266 sess.retrier = util.NewAutoRedialer(sess)332 sess.retrier = util.NewAutoRedialer(sess)
333 sess.lastAutoRedial = time.Now()
267 go func() { doneCh <- sess.retrier.Redial() }()334 go func() { doneCh <- sess.retrier.Redial() }()
268}335}
269336
@@ -289,6 +356,7 @@
289 err := sess.proto.WriteMessage(protocol.PingPongMsg{Type: "pong"})356 err := sess.proto.WriteMessage(protocol.PingPongMsg{Type: "pong"})
290 if err == nil {357 if err == nil {
291 sess.Log.Debugf("ping.")358 sess.Log.Debugf("ping.")
359 sess.clearShouldDelay()
292 } else {360 } else {
293 sess.setState(Error)361 sess.setState(Error)
294 sess.Log.Errorf("unable to pong: %s", err)362 sess.Log.Errorf("unable to pong: %s", err)
@@ -330,6 +398,7 @@
330 sess.Log.Errorf("unable to ack broadcast: %s", err)398 sess.Log.Errorf("unable to ack broadcast: %s", err)
331 return err399 return err
332 }400 }
401 sess.clearShouldDelay()
333 sess.Log.Debugf("broadcast chan:%v app:%v topLevel:%d payloads:%s",402 sess.Log.Debugf("broadcast chan:%v app:%v topLevel:%d payloads:%s",
334 bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads)403 bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads)
335 if bcast.ChanId == protocol.SystemChannelId {404 if bcast.ChanId == protocol.SystemChannelId {
@@ -409,10 +478,9 @@
409 return err478 return err
410 }479 }
411 err = proto.WriteMessage(protocol.ConnectMsg{480 err = proto.WriteMessage(protocol.ConnectMsg{
412 Type: "connect",481 Type: "connect",
413 DeviceId: sess.DeviceId,482 DeviceId: sess.DeviceId,
414 // xxx get the SSO Authorization string from the phone483 Authorization: sess.auth,
415 Authorization: "",
416 Levels: levels,484 Levels: levels,
417 Info: sess.Info,485 Info: sess.Info,
418 })486 })
@@ -447,13 +515,15 @@
447515
448// run calls connect, and if it works it calls start, and if it works516// run calls connect, and if it works it calls start, and if it works
449// it runs loop in a goroutine, and ships its return value over ErrCh.517// it runs loop in a goroutine, and ships its return value over ErrCh.
450func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error {518func (sess *ClientSession) run(closer func(), authChecker, hostGetter, connecter, starter, looper func() error) error {
451 closer()519 closer()
452 err := hostGetter()520 if err := authChecker(); err != nil {
453 if err != nil {521 return err
454 return err522 }
455 }523 if err := hostGetter(); err != nil {
456 err = connecter()524 return err
525 }
526 err := connecter()
457 if err == nil {527 if err == nil {
458 err = starter()528 err = starter()
459 if err == nil {529 if err == nil {
@@ -483,7 +553,7 @@
483 // keep on trying.553 // keep on trying.
484 panic("can't Dial() without a protocol constructor.")554 panic("can't Dial() without a protocol constructor.")
485 }555 }
486 return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop)556 return sess.run(sess.doClose, sess.checkAuthorization, sess.getHosts, sess.connect, sess.start, sess.loop)
487}557}
488558
489func init() {559func init() {
490560
=== modified file 'client/session/session_test.go'
--- client/session/session_test.go 2014-04-04 13:55:00 +0000
+++ client/session/session_test.go 2014-04-29 18:02:00 +0000
@@ -32,12 +32,12 @@
3232
33 . "launchpad.net/gocheck"33 . "launchpad.net/gocheck"
3434
35 "launchpad.net/ubuntu-push/client/gethosts"
35 "launchpad.net/ubuntu-push/client/session/levelmap"36 "launchpad.net/ubuntu-push/client/session/levelmap"
36 //"launchpad.net/ubuntu-push/client/gethosts"
37 "launchpad.net/ubuntu-push/logger"
38 "launchpad.net/ubuntu-push/protocol"37 "launchpad.net/ubuntu-push/protocol"
39 helpers "launchpad.net/ubuntu-push/testing"38 helpers "launchpad.net/ubuntu-push/testing"
40 "launchpad.net/ubuntu-push/testing/condition"39 "launchpad.net/ubuntu-push/testing/condition"
40 "launchpad.net/ubuntu-push/util"
41)41)
4242
43func TestSession(t *testing.T) { TestingT(t) }43func TestSession(t *testing.T) { TestingT(t) }
@@ -165,14 +165,26 @@
165/////165/////
166166
167type clientSessionSuite struct {167type clientSessionSuite struct {
168 log logger.Logger168 log *helpers.TestLogger
169 lvls func() (levelmap.LevelMap, error)169 lvls func() (levelmap.LevelMap, error)
170}170}
171171
172func (cs *clientSessionSuite) SetUpSuite(c *C) {
173 getAuthorization = func() (string, error) {
174 return "some auth", nil
175 }
176 shouldGetAuth = true
177}
178
172func (cs *clientSessionSuite) SetUpTest(c *C) {179func (cs *clientSessionSuite) SetUpTest(c *C) {
173 cs.log = helpers.NewTestLogger(c, "debug")180 cs.log = helpers.NewTestLogger(c, "debug")
174}181}
175182
183func (cs *clientSessionSuite) TearDownSuite(c *C) {
184 getAuthorization = util.GetAuthorization
185 shouldGetAuth = false
186}
187
176// in-memory level map testing188// in-memory level map testing
177var _ = Suite(&clientSessionSuite{lvls: levelmap.NewLevelMap})189var _ = Suite(&clientSessionSuite{lvls: levelmap.NewLevelMap})
178190
@@ -182,6 +194,7 @@
182var _ = Suite(&clientSqlevelsSessionSuite{})194var _ = Suite(&clientSqlevelsSessionSuite{})
183195
184func (cs *clientSqlevelsSessionSuite) SetUpSuite(c *C) {196func (cs *clientSqlevelsSessionSuite) SetUpSuite(c *C) {
197 cs.clientSessionSuite.SetUpSuite(c)
185 cs.lvls = func() (levelmap.LevelMap, error) { return levelmap.NewSqliteLevelMap(":memory:") }198 cs.lvls = func() (levelmap.LevelMap, error) { return levelmap.NewSqliteLevelMap(":memory:") }
186}199}
187200
@@ -214,6 +227,10 @@
214 c.Check(sess, NotNil)227 c.Check(sess, NotNil)
215 c.Check(err, IsNil)228 c.Check(err, IsNil)
216 c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"})229 c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"})
230 // the session is happy and redial delayer is default
231 c.Check(sess.ShouldDelay(), Equals, false)
232 c.Check(fmt.Sprintf("%#v", sess.redialDelay), Equals, fmt.Sprintf("%#v", redialDelay))
233 c.Check(sess.redialDelays, DeepEquals, util.Timeouts())
217 // but no root CAs set234 // but no root CAs set
218 c.Check(sess.TLS.RootCAs, IsNil)235 c.Check(sess.TLS.RootCAs, IsNil)
219 c.Check(sess.State(), Equals, Disconnected)236 c.Check(sess.State(), Equals, Disconnected)
@@ -264,16 +281,17 @@
264}281}
265282
266type testHostGetter struct {283type testHostGetter struct {
267 hosts []string284 domain string
268 err error285 hosts []string
286 err error
269}287}
270288
271func (thg *testHostGetter) Get() ([]string, error) {289func (thg *testHostGetter) Get() (*gethosts.Host, error) {
272 return thg.hosts, thg.err290 return &gethosts.Host{thg.domain, thg.hosts}, thg.err
273}291}
274292
275func (cs *clientSessionSuite) TestGetHostsRemote(c *C) {293func (cs *clientSessionSuite) TestGetHostsRemote(c *C) {
276 hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}294 hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil}
277 sess := &ClientSession{getHost: hostGetter, timeSince: time.Since}295 sess := &ClientSession{getHost: hostGetter, timeSince: time.Since}
278 err := sess.getHosts()296 err := sess.getHosts()
279 c.Assert(err, IsNil)297 c.Assert(err, IsNil)
@@ -284,7 +302,7 @@
284 sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)302 sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)
285 c.Assert(err, IsNil)303 c.Assert(err, IsNil)
286 hostsErr := errors.New("failed")304 hostsErr := errors.New("failed")
287 hostGetter := &testHostGetter{nil, hostsErr}305 hostGetter := &testHostGetter{"", nil, hostsErr}
288 sess.getHost = hostGetter306 sess.getHost = hostGetter
289 err = sess.getHosts()307 err = sess.getHosts()
290 c.Assert(err, Equals, hostsErr)308 c.Assert(err, Equals, hostsErr)
@@ -293,7 +311,7 @@
293}311}
294312
295func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) {313func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) {
296 hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}314 hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil}
297 sess := &ClientSession{315 sess := &ClientSession{
298 getHost: hostGetter,316 getHost: hostGetter,
299 ClientSessionConfig: ClientSessionConfig{317 ClientSessionConfig: ClientSessionConfig{
@@ -318,7 +336,7 @@
318}336}
319337
320func (cs *clientSessionSuite) TestGetHostsRemoteCachingReset(c *C) {338func (cs *clientSessionSuite) TestGetHostsRemoteCachingReset(c *C) {
321 hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}339 hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil}
322 sess := &ClientSession{340 sess := &ClientSession{
323 getHost: hostGetter,341 getHost: hostGetter,
324 ClientSessionConfig: ClientSessionConfig{342 ClientSessionConfig: ClientSessionConfig{
@@ -341,6 +359,18 @@
341}359}
342360
343/****************************************************************361/****************************************************************
362 checkAuthorization() tests
363****************************************************************/
364
365func (cs *clientSessionSuite) TestChecksAuthorizationFromServer(c *C) {
366 sess := &ClientSession{}
367 c.Assert(sess.auth, Equals, "")
368 err := sess.checkAuthorization()
369 c.Assert(err, IsNil)
370 c.Check(sess.auth, Equals, "some auth")
371}
372
373/****************************************************************
344 startConnectionAttempt()/nextHostToTry()/started tests374 startConnectionAttempt()/nextHostToTry()/started tests
345****************************************************************/375****************************************************************/
346376
@@ -427,7 +457,9 @@
427 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)457 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
428 c.Assert(err, IsNil)458 c.Assert(err, IsNil)
429 sess.deliveryHosts = []string{"nowhere"}459 sess.deliveryHosts = []string{"nowhere"}
460 sess.clearShouldDelay()
430 err = sess.connect()461 err = sess.connect()
462 c.Check(sess.ShouldDelay(), Equals, true)
431 c.Check(err, ErrorMatches, ".*connect.*address.*")463 c.Check(err, ErrorMatches, ".*connect.*address.*")
432 c.Check(sess.State(), Equals, Error)464 c.Check(sess.State(), Equals, Error)
433}465}
@@ -439,7 +471,9 @@
439 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)471 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
440 c.Assert(err, IsNil)472 c.Assert(err, IsNil)
441 sess.deliveryHosts = []string{srv.Addr().String()}473 sess.deliveryHosts = []string{srv.Addr().String()}
474 sess.clearShouldDelay()
442 err = sess.connect()475 err = sess.connect()
476 c.Check(sess.ShouldDelay(), Equals, true)
443 c.Check(err, IsNil)477 c.Check(err, IsNil)
444 c.Check(sess.Connection, NotNil)478 c.Check(sess.Connection, NotNil)
445 c.Check(sess.State(), Equals, Connected)479 c.Check(sess.State(), Equals, Connected)
@@ -452,7 +486,9 @@
452 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)486 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
453 c.Assert(err, IsNil)487 c.Assert(err, IsNil)
454 sess.deliveryHosts = []string{"nowhere", srv.Addr().String()}488 sess.deliveryHosts = []string{"nowhere", srv.Addr().String()}
489 sess.clearShouldDelay()
455 err = sess.connect()490 err = sess.connect()
491 c.Check(sess.ShouldDelay(), Equals, true)
456 c.Check(err, IsNil)492 c.Check(err, IsNil)
457 c.Check(sess.Connection, NotNil)493 c.Check(sess.Connection, NotNil)
458 c.Check(sess.State(), Equals, Connected)494 c.Check(sess.State(), Equals, Connected)
@@ -466,7 +502,9 @@
466 srv.Close()502 srv.Close()
467 c.Assert(err, IsNil)503 c.Assert(err, IsNil)
468 sess.deliveryHosts = []string{srv.Addr().String()}504 sess.deliveryHosts = []string{srv.Addr().String()}
505 sess.clearShouldDelay()
469 err = sess.connect()506 err = sess.connect()
507 c.Check(sess.ShouldDelay(), Equals, true)
470 c.Check(err, ErrorMatches, ".*connection refused")508 c.Check(err, ErrorMatches, ".*connection refused")
471 c.Check(sess.State(), Equals, Error)509 c.Check(sess.State(), Equals, Error)
472}510}
@@ -548,6 +586,27 @@
548 c.Check(<-ch, Not(Equals), 0)586 c.Check(<-ch, Not(Equals), 0)
549}587}
550588
589func (cs *clientSessionSuite) TestAutoRedialCallsRedialDelay(c *C) {
590 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
591 c.Assert(err, IsNil)
592 flag := false
593 sess.redialDelay = func(sess *ClientSession) time.Duration { flag = true; return 0 }
594 sess.AutoRedial(nil)
595 c.Check(flag, Equals, true)
596}
597
598func (cs *clientSessionSuite) TestAutoRedialSetsRedialDelayIfTooQuick(c *C) {
599 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
600 c.Assert(err, IsNil)
601 sess.redialDelay = func(sess *ClientSession) time.Duration { return 0 }
602 sess.AutoRedial(nil)
603 c.Check(sess.ShouldDelay(), Equals, false)
604 sess.stopRedial()
605 sess.clearShouldDelay()
606 sess.AutoRedial(nil)
607 c.Check(sess.ShouldDelay(), Equals, true)
608}
609
551/****************************************************************610/****************************************************************
552 handlePing() tests611 handlePing() tests
553****************************************************************/612****************************************************************/
@@ -594,6 +653,24 @@
594 c.Check(s.sess.State(), Equals, Error)653 c.Check(s.sess.State(), Equals, Error)
595}654}
596655
656func (s *msgSuite) TestHandlePingClearsDelay(c *C) {
657 s.sess.setShouldDelay()
658 s.upCh <- nil // no error
659 c.Check(s.sess.handlePing(), IsNil)
660 c.Assert(len(s.downCh), Equals, 1)
661 c.Check(<-s.downCh, Equals, protocol.PingPongMsg{Type: "pong"})
662 c.Check(s.sess.ShouldDelay(), Equals, false)
663}
664
665func (s *msgSuite) TestHandlePingDoesNotClearsDelayOnError(c *C) {
666 s.sess.setShouldDelay()
667 s.upCh <- errors.New("Pong")
668 c.Check(s.sess.handlePing(), NotNil)
669 c.Assert(len(s.downCh), Equals, 1)
670 c.Check(<-s.downCh, Equals, protocol.PingPongMsg{Type: "pong"})
671 c.Check(s.sess.ShouldDelay(), Equals, true)
672}
673
597/****************************************************************674/****************************************************************
598 handleBroadcast() tests675 handleBroadcast() tests
599****************************************************************/676****************************************************************/
@@ -687,6 +764,32 @@
687 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"nak"})764 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"nak"})
688}765}
689766
767func (s *msgSuite) TestHandleBroadcastClearsDelay(c *C) {
768 s.sess.setShouldDelay()
769
770 msg := serverMsg{"broadcast", protocol.BroadcastMsg{},
771 protocol.NotificationsMsg{}, protocol.ConnBrokenMsg{}}
772 go func() { s.errCh <- s.sess.handleBroadcast(&msg) }()
773 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
774 s.upCh <- nil // ack ok
775 c.Check(<-s.errCh, IsNil)
776
777 c.Check(s.sess.ShouldDelay(), Equals, false)
778}
779
780func (s *msgSuite) TestHandleBroadcastDoesNotClearDelayOnError(c *C) {
781 s.sess.setShouldDelay()
782
783 msg := serverMsg{"broadcast", protocol.BroadcastMsg{},
784 protocol.NotificationsMsg{}, protocol.ConnBrokenMsg{}}
785 go func() { s.errCh <- s.sess.handleBroadcast(&msg) }()
786 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
787 s.upCh <- errors.New("bcast")
788 c.Check(<-s.errCh, NotNil)
789
790 c.Check(s.sess.ShouldDelay(), Equals, true)
791}
792
690/****************************************************************793/****************************************************************
691 handleConnBroken() tests794 handleConnBroken() tests
692****************************************************************/795****************************************************************/
@@ -852,9 +955,10 @@
852955
853 c.Check(takeNext(downCh), Equals, "deadline 0")956 c.Check(takeNext(downCh), Equals, "deadline 0")
854 c.Check(takeNext(downCh), DeepEquals, protocol.ConnectMsg{957 c.Check(takeNext(downCh), DeepEquals, protocol.ConnectMsg{
855 Type: "connect",958 Type: "connect",
856 DeviceId: sess.DeviceId,959 DeviceId: sess.DeviceId,
857 Levels: map[string]int64{},960 Levels: map[string]int64{},
961 Authorization: "",
858 })962 })
859 upCh <- errors.New("Overflow error in /dev/null")963 upCh <- errors.New("Overflow error in /dev/null")
860 err = <-errCh964 err = <-errCh
@@ -959,6 +1063,7 @@
959 msg, ok := takeNext(downCh).(protocol.ConnectMsg)1063 msg, ok := takeNext(downCh).(protocol.ConnectMsg)
960 c.Check(ok, Equals, true)1064 c.Check(ok, Equals, true)
961 c.Check(msg.DeviceId, Equals, "wah")1065 c.Check(msg.DeviceId, Equals, "wah")
1066 c.Check(msg.Authorization, Equals, "")
962 c.Check(msg.Info, DeepEquals, info)1067 c.Check(msg.Info, DeepEquals, info)
963 upCh <- nil // no error1068 upCh <- nil // no error
964 upCh <- protocol.ConnAckMsg{1069 upCh <- protocol.ConnAckMsg{
@@ -975,6 +1080,22 @@
975 run() tests1080 run() tests
976****************************************************************/1081****************************************************************/
9771082
1083func (cs *clientSessionSuite) TestRunBailsIfAuthCheckFails(c *C) {
1084 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1085 c.Assert(err, IsNil)
1086 failure := errors.New("TestRunBailsIfAuthCheckFails")
1087 has_closed := false
1088 err = sess.run(
1089 func() { has_closed = true },
1090 func() error { return failure },
1091 nil,
1092 nil,
1093 nil,
1094 nil)
1095 c.Check(err, Equals, failure)
1096 c.Check(has_closed, Equals, true)
1097}
1098
978func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) {1099func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) {
979 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)1100 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
980 c.Assert(err, IsNil)1101 c.Assert(err, IsNil)
@@ -982,6 +1103,7 @@
982 has_closed := false1103 has_closed := false
983 err = sess.run(1104 err = sess.run(
984 func() { has_closed = true },1105 func() { has_closed = true },
1106 func() error { return nil },
985 func() error { return failure },1107 func() error { return failure },
986 nil,1108 nil,
987 nil,1109 nil,
@@ -997,6 +1119,7 @@
997 err = sess.run(1119 err = sess.run(
998 func() {},1120 func() {},
999 func() error { return nil },1121 func() error { return nil },
1122 func() error { return nil },
1000 func() error { return failure },1123 func() error { return failure },
1001 nil,1124 nil,
1002 nil)1125 nil)
@@ -1011,6 +1134,7 @@
1011 func() {},1134 func() {},
1012 func() error { return nil },1135 func() error { return nil },
1013 func() error { return nil },1136 func() error { return nil },
1137 func() error { return nil },
1014 func() error { return failure },1138 func() error { return failure },
1015 nil)1139 nil)
1016 c.Check(err, Equals, failure)1140 c.Check(err, Equals, failure)
@@ -1030,6 +1154,7 @@
1030 func() error { return nil },1154 func() error { return nil },
1031 func() error { return nil },1155 func() error { return nil },
1032 func() error { return nil },1156 func() error { return nil },
1157 func() error { return nil },
1033 func() error { sess.MsgCh <- notf; return <-failureCh })1158 func() error { sess.MsgCh <- notf; return <-failureCh })
1034 c.Check(err, Equals, nil)1159 c.Check(err, Equals, nil)
1035 // if run doesn't error it sets up the channels1160 // if run doesn't error it sets up the channels
@@ -1087,9 +1212,64 @@
10871212
1088var (1213var (
1089 dialTestTimeout = 100 * time.Millisecond1214 dialTestTimeout = 100 * time.Millisecond
1090 dialTestConf = ClientSessionConfig{ExchangeTimeout: dialTestTimeout}1215 dialTestConf = ClientSessionConfig{
1216 ExchangeTimeout: dialTestTimeout,
1217 PEM: helpers.TestCertPEMBlock,
1218 }
1091)1219)
10921220
1221func (cs *clientSessionSuite) TestDialBadServerName(c *C) {
1222 // a borked server name
1223 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
1224 c.Assert(err, IsNil)
1225 tlsCfg := &tls.Config{
1226 Certificates: []tls.Certificate{cert},
1227 SessionTicketsDisabled: true,
1228 }
1229
1230 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
1231 c.Assert(err, IsNil)
1232 // advertise
1233 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1234 b, err := json.Marshal(map[string]interface{}{
1235 "domain": "xyzzy", // <-- *** THIS *** is the bit that'll break it
1236 "hosts": []string{"nowhere", lst.Addr().String()},
1237 })
1238 if err != nil {
1239 panic(err)
1240 }
1241 w.Header().Set("Content-Type", "application/json")
1242 w.Write(b)
1243 }))
1244 defer ts.Close()
1245
1246 sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log)
1247 c.Assert(err, IsNil)
1248 tconn := &testConn{}
1249 sess.Connection = tconn
1250
1251 upCh := make(chan interface{}, 5)
1252 downCh := make(chan interface{}, 5)
1253 errCh := make(chan error, 1)
1254 proto := &testProtocol{up: upCh, down: downCh}
1255 sess.Protocolator = func(net.Conn) protocol.Protocol { return proto }
1256
1257 go func() {
1258 errCh <- sess.Dial()
1259 }()
1260
1261 srv, err := lst.Accept()
1262 c.Assert(err, IsNil)
1263
1264 // connect done
1265
1266 _, err = protocol.ReadWireFormatVersion(srv, dialTestTimeout)
1267 c.Check(err, NotNil)
1268
1269 c.Check(<-errCh, NotNil)
1270 c.Check(sess.State(), Equals, Error)
1271}
1272
1093func (cs *clientSessionSuite) TestDialWorks(c *C) {1273func (cs *clientSessionSuite) TestDialWorks(c *C) {
1094 // happy path thoughts1274 // happy path thoughts
1095 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)1275 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
@@ -1104,7 +1284,8 @@
1104 // advertise1284 // advertise
1105 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1285 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1106 b, err := json.Marshal(map[string]interface{}{1286 b, err := json.Marshal(map[string]interface{}{
1107 "hosts": []string{"nowhere", lst.Addr().String()},1287 "domain": "localhost",
1288 "hosts": []string{"nowhere", lst.Addr().String()},
1108 })1289 })
1109 if err != nil {1290 if err != nil {
1110 panic(err)1291 panic(err)
@@ -1223,3 +1404,38 @@
1223 c.Assert(err, IsNil)1404 c.Assert(err, IsNil)
1224 // connect done1405 // connect done
1225}1406}
1407
1408/****************************************************************
1409 redialDelay() tests
1410****************************************************************/
1411
1412func (cs *clientSessionSuite) TestShouldDelay(c *C) {
1413 sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log)
1414 c.Assert(err, IsNil)
1415 c.Check(sess.ShouldDelay(), Equals, false)
1416 sess.setShouldDelay()
1417 c.Check(sess.ShouldDelay(), Equals, true)
1418 sess.clearShouldDelay()
1419 c.Check(sess.ShouldDelay(), Equals, false)
1420}
1421
1422func (cs *clientSessionSuite) TestRedialDelay(c *C) {
1423 sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log)
1424 c.Assert(err, IsNil)
1425 sess.redialDelays = []time.Duration{17, 42}
1426 n := 0
1427 sess.redialJitter = func(time.Duration) time.Duration { n++; return 0 }
1428 // we get increasing delays while we're unhappy
1429 sess.setShouldDelay()
1430 c.Check(redialDelay(sess), Equals, time.Duration(17))
1431 c.Check(redialDelay(sess), Equals, time.Duration(42))
1432 c.Check(redialDelay(sess), Equals, time.Duration(42))
1433 // once we're happy, delays drop to 0
1434 sess.clearShouldDelay()
1435 c.Check(redialDelay(sess), Equals, time.Duration(0))
1436 // and start again from the top if we become unhappy again
1437 sess.setShouldDelay()
1438 c.Check(redialDelay(sess), Equals, time.Duration(17))
1439 // and redialJitter got called every time shouldDelay was true
1440 c.Check(n, Equals, 4)
1441}
12261442
=== modified file 'config/config.go'
--- config/config.go 2014-03-25 18:49:18 +0000
+++ config/config.go 2014-04-29 18:02:00 +0000
@@ -20,6 +20,7 @@
20import (20import (
21 "encoding/json"21 "encoding/json"
22 "errors"22 "errors"
23 "flag"
23 "fmt"24 "fmt"
24 "io"25 "io"
25 "io/ioutil"26 "io/ioutil"
@@ -27,6 +28,7 @@
27 "os"28 "os"
28 "path/filepath"29 "path/filepath"
29 "reflect"30 "reflect"
31 "strconv"
30 "strings"32 "strings"
31 "time"33 "time"
32)34)
@@ -118,6 +120,22 @@
118 return fillDestConfig(destValue, p1)120 return fillDestConfig(destValue, p1)
119}121}
120122
123// FromString are config holders that can be set by parsing a string.
124type FromString interface {
125 SetFromString(enc string) error
126}
127
128// UnmarshalJSONViaString helps unmarshalling from JSON for FromString
129// supporting config holders.
130func UnmarshalJSONViaString(dest FromString, b []byte) error {
131 var enc string
132 err := json.Unmarshal(b, &enc)
133 if err != nil {
134 return err
135 }
136 return dest.SetFromString(enc)
137}
138
121// ConfigTimeDuration can hold a time.Duration in a configuration struct,139// ConfigTimeDuration can hold a time.Duration in a configuration struct,
122// that is parsed from a string as supported by time.ParseDuration.140// that is parsed from a string as supported by time.ParseDuration.
123type ConfigTimeDuration struct {141type ConfigTimeDuration struct {
@@ -125,13 +143,11 @@
125}143}
126144
127func (ctd *ConfigTimeDuration) UnmarshalJSON(b []byte) error {145func (ctd *ConfigTimeDuration) UnmarshalJSON(b []byte) error {
128 var enc string146 return UnmarshalJSONViaString(ctd, b)
129 var v time.Duration147}
130 err := json.Unmarshal(b, &enc)148
131 if err != nil {149func (ctd *ConfigTimeDuration) SetFromString(enc string) error {
132 return err150 v, err := time.ParseDuration(enc)
133 }
134 v, err = time.ParseDuration(enc)
135 if err != nil {151 if err != nil {
136 return err152 return err
137 }153 }
@@ -148,12 +164,11 @@
148type ConfigHostPort string164type ConfigHostPort string
149165
150func (chp *ConfigHostPort) UnmarshalJSON(b []byte) error {166func (chp *ConfigHostPort) UnmarshalJSON(b []byte) error {
151 var enc string167 return UnmarshalJSONViaString(chp, b)
152 err := json.Unmarshal(b, &enc)168}
153 if err != nil {169
154 return err170func (chp *ConfigHostPort) SetFromString(enc string) error {
155 }171 _, _, err := net.SplitHostPort(enc)
156 _, _, err = net.SplitHostPort(enc)
157 if err != nil {172 if err != nil {
158 return err173 return err
159 }174 }
@@ -198,23 +213,117 @@
198 return ioutil.ReadFile(p)213 return ioutil.ReadFile(p)
199}214}
200215
201// ReadFiles reads configuration from a set of files. Uses ReadConfig internally.216// used to implement getting config values with flag.Parse()
217type val struct {
218 destField destField
219 accu map[string]json.RawMessage
220}
221
222func (v *val) String() string { // used to show default
223 return string(v.accu[v.destField.configName()])
224}
225
226func (v *val) IsBoolFlag() bool {
227 return v.destField.fld.Type.Kind() == reflect.Bool
228}
229
230func (v *val) marshalAsNeeded(s string) (json.RawMessage, error) {
231 var toMarshal interface{}
232 switch v.destField.dest.(type) {
233 case *string, FromString:
234 toMarshal = s
235 case *bool:
236 bit, err := strconv.ParseBool(s)
237 if err != nil {
238 return nil, err
239 }
240 toMarshal = bit
241 default:
242 return json.RawMessage(s), nil
243 }
244 return json.Marshal(toMarshal)
245}
246
247func (v *val) Set(s string) error {
248 marshalled, err := v.marshalAsNeeded(s)
249 if err != nil {
250 return err
251 }
252 v.accu[v.destField.configName()] = marshalled
253 return nil
254}
255
256func readOneConfig(accu map[string]json.RawMessage, cfgPath string) error {
257 r, err := os.Open(cfgPath)
258 if err != nil {
259 return err
260 }
261 defer r.Close()
262 err = json.NewDecoder(r).Decode(&accu)
263 if err != nil {
264 return err
265 }
266 return nil
267}
268
269// used to implement -cfg@=
270type readConfigAtVal struct {
271 accu map[string]json.RawMessage
272}
273
274func (v *readConfigAtVal) String() string {
275 return "<config.json>"
276}
277
278func (v *readConfigAtVal) Set(path string) error {
279 return readOneConfig(v.accu, path)
280}
281
282// readUsingFlags gets config values from command line flags.
283func readUsingFlags(accu map[string]json.RawMessage, destValue reflect.Value) error {
284 if flag.Parsed() {
285 if IgnoreParsedFlags {
286 return nil
287 }
288 return fmt.Errorf("too late, flags already parsed")
289 }
290 destStruct := destValue.Elem()
291 for destField := range traverseStruct(destStruct) {
292 help := destField.fld.Tag.Get("help")
293 flag.Var(&val{destField, accu}, destField.configName(), help)
294 }
295 flag.Var(&readConfigAtVal{accu}, "cfg@", "get config values from file")
296 flag.Parse()
297 return nil
298}
299
300// IgnoreParsedFlags will just have ReadFiles ignore <flags> if the
301// command line was already parsed.
302var IgnoreParsedFlags = false
303
304// ReadFiles reads configuration from a set of files. The string
305// "<flags>" can be used as a pseudo file-path, it will consider
306// command line flags, invoking flag.Parse(). Among those the flag
307// -cfg@=FILE can be used to get further config values from FILE.
202func ReadFiles(destConfig interface{}, cfgFpaths ...string) error {308func ReadFiles(destConfig interface{}, cfgFpaths ...string) error {
203 destValue, err := checkDestConfig("destConfig", destConfig)309 destValue, err := checkDestConfig("destConfig", destConfig)
204 if err != nil {310 if err != nil {
205 return err311 return err
206 }312 }
207 // do the parsing in two phases for better error handling313 // do the parsing in two phases for better error handling
208 var p1 map[string]json.RawMessage314 p1 := make(map[string]json.RawMessage)
209 readOne := false315 readOne := false
210 for _, cfgPath := range cfgFpaths {316 for _, cfgPath := range cfgFpaths {
317 if cfgPath == "<flags>" {
318 err := readUsingFlags(p1, destValue)
319 if err != nil {
320 return err
321 }
322 readOne = true
323 continue
324 }
211 if _, err := os.Stat(cfgPath); err == nil {325 if _, err := os.Stat(cfgPath); err == nil {
212 r, err := os.Open(cfgPath)326 err := readOneConfig(p1, cfgPath)
213 if err != nil {
214 return err
215 }
216 defer r.Close()
217 err = json.NewDecoder(r).Decode(&p1)
218 if err != nil {327 if err != nil {
219 return err328 return err
220 }329 }
221330
=== modified file 'config/config_test.go'
--- config/config_test.go 2014-03-25 18:49:18 +0000
+++ config/config_test.go 2014-04-29 18:02:00 +0000
@@ -18,6 +18,9 @@
1818
19import (19import (
20 "bytes"20 "bytes"
21 "encoding/json"
22 "flag"
23 "fmt"
21 "io/ioutil"24 "io/ioutil"
22 "os"25 "os"
23 "path/filepath"26 "path/filepath"
@@ -230,3 +233,105 @@
230 c.Check(res, DeepEquals, []string{"b", "c_list", "d"})233 c.Check(res, DeepEquals, []string{"b", "c_list", "d"})
231234
232}235}
236
237type testConfig3 struct {
238 A bool
239 B string
240 C []string `json:"c_list"`
241 D ConfigTimeDuration `help:"duration"`
242 E ConfigHostPort
243 F string
244}
245
246type configFlagsSuite struct{}
247
248var _ = Suite(&configFlagsSuite{})
249
250func (s *configFlagsSuite) SetUpTest(c *C) {
251 flag.CommandLine = flag.NewFlagSet("cmd", flag.PanicOnError)
252 // supress outputs
253 flag.Usage = func() { flag.PrintDefaults() }
254 flag.CommandLine.SetOutput(ioutil.Discard)
255}
256
257func (s *configFlagsSuite) TestReadUsingFlags(c *C) {
258 os.Args = []string{"cmd", "-a=1", "-b=foo", "-c_list", `["x","y"]`, "-d", "10s", "-e=localhost:80"}
259 var cfg testConfig3
260 p := make(map[string]json.RawMessage)
261 err := readUsingFlags(p, reflect.ValueOf(&cfg))
262 c.Assert(err, IsNil)
263 c.Check(p, DeepEquals, map[string]json.RawMessage{
264 "a": json.RawMessage("true"),
265 "b": json.RawMessage(`"foo"`),
266 "c_list": json.RawMessage(`["x","y"]`),
267 "d": json.RawMessage(`"10s"`),
268 "e": json.RawMessage(`"localhost:80"`),
269 })
270}
271
272func (s *configFlagsSuite) TestReadUsingFlagsBoolError(c *C) {
273 os.Args = []string{"cmd", "-a=zoo"}
274 var cfg testConfig3
275 p := make(map[string]json.RawMessage)
276 c.Check(func() { readUsingFlags(p, reflect.ValueOf(&cfg)) }, PanicMatches, ".*invalid boolean.*-a.*")
277}
278
279func (s *configFlagsSuite) TestReadFilesAndFlags(c *C) {
280 // test <flags> pseudo file
281 os.Args = []string{"cmd", "-b=x"}
282 tmpDir := c.MkDir()
283 cfgPath := filepath.Join(tmpDir, "cfg.json")
284 err := ioutil.WriteFile(cfgPath, []byte(`{"a": 42, "c_list": ["y", "z"]}`), os.ModePerm)
285 c.Assert(err, IsNil)
286 var cfg testConfig1
287 err = ReadFiles(&cfg, cfgPath, "<flags>")
288 c.Assert(err, IsNil)
289 c.Check(cfg.A, Equals, 42)
290 c.Check(cfg.B, Equals, "x")
291 c.Check(cfg.C, DeepEquals, []string{"y", "z"})
292}
293
294func (s *configFlagsSuite) TestReadFilesAndFlagsConfigAtSupport(c *C) {
295 // test <flags> pseudo file
296 tmpDir := c.MkDir()
297 cfgPath := filepath.Join(tmpDir, "cfg.json")
298 os.Args = []string{"cmd", "-a=42", fmt.Sprintf("-cfg@=%s", cfgPath)}
299 err := ioutil.WriteFile(cfgPath, []byte(`{"b": "x", "c_list": ["y", "z"]}`), os.ModePerm)
300 c.Assert(err, IsNil)
301 var cfg testConfig1
302 err = ReadFiles(&cfg, "<flags>")
303 c.Assert(err, IsNil)
304 c.Check(cfg.A, Equals, 42)
305 c.Check(cfg.B, Equals, "x")
306 c.Check(cfg.C, DeepEquals, []string{"y", "z"})
307}
308
309func (s *configFlagsSuite) TestReadUsingFlagsHelp(c *C) {
310 os.Args = []string{"cmd", "-h"}
311 buf := bytes.NewBufferString("")
312 flag.CommandLine.Init("cmd", flag.ContinueOnError)
313 flag.CommandLine.SetOutput(buf)
314 var cfg testConfig3
315 p := map[string]json.RawMessage{
316 "d": json.RawMessage(`"2s"`),
317 }
318 readUsingFlags(p, reflect.ValueOf(&cfg))
319 c.Check(buf.String(), Matches, `(?s).*-cfg@=<config.json>: get config values from file\n.*-d="2s": duration.*`)
320}
321
322func (s *configFlagsSuite) TestReadUsingFlagsAlreadyParsed(c *C) {
323 os.Args = []string{"cmd"}
324 flag.Parse()
325 var cfg struct{}
326 p := make(map[string]json.RawMessage)
327 err := readUsingFlags(p, reflect.ValueOf(&cfg))
328 c.Assert(err, ErrorMatches, "too late, flags already parsed")
329 err = ReadFiles(&cfg, "<flags>")
330 c.Assert(err, ErrorMatches, "too late, flags already parsed")
331 IgnoreParsedFlags = true
332 defer func() {
333 IgnoreParsedFlags = false
334 }()
335 err = ReadFiles(&cfg, "<flags>")
336 c.Assert(err, IsNil)
337}
233338
=== modified file 'debian/changelog'
--- debian/changelog 2014-04-11 18:31:57 +0000
+++ debian/changelog 2014-04-29 18:02:00 +0000
@@ -1,3 +1,9 @@
1ubuntu-push (0.21-0.ubuntu1) UNRELEASED; urgency=medium
2
3 * New upstream release: first auth bits, and Qt dependency.
4
5 -- John Lenton <john.lenton@canonical.com> Tue, 15 Apr 2014 14:04:35 +0100
6
1ubuntu-push (0.2+14.04.20140411-0ubuntu1) trusty; urgency=medium7ubuntu-push (0.2+14.04.20140411-0ubuntu1) trusty; urgency=medium
28
3 [ John Lenton ]9 [ John Lenton ]
410
=== modified file 'debian/control'
--- debian/control 2014-03-25 16:26:20 +0000
+++ debian/control 2014-04-29 18:02:00 +0000
@@ -14,6 +14,10 @@
14 libgcrypt11-dev,14 libgcrypt11-dev,
15 libglib2.0-dev (>= 2.31.6),15 libglib2.0-dev (>= 2.31.6),
16 libwhoopsie-dev,16 libwhoopsie-dev,
17 qtbase5-private-dev,
18 qtdeclarative5-dev,
19 libqt5opengl5-dev,
20 libubuntuoneauth-2.0-dev,
17Standards-Version: 3.9.521Standards-Version: 3.9.5
18Homepage: http://launchpad.net/ubuntu-push22Homepage: http://launchpad.net/ubuntu-push
19Vcs-Bzr: lp:ubuntu-push23Vcs-Bzr: lp:ubuntu-push
2024
=== modified file 'debian/rules'
--- debian/rules 2014-03-24 12:22:55 +0000
+++ debian/rules 2014-04-29 18:02:00 +0000
@@ -2,9 +2,12 @@
2# -*- makefile -*-2# -*- makefile -*-
33
4export DH_GOPKG := launchpad.net/ubuntu-push4export DH_GOPKG := launchpad.net/ubuntu-push
5export DEB_BUILD_OPTIONS := nostrip
6export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR)5export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR)
76
7override_dh_auto_build:
8 cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1) && tar xvzf ../../externals.tgz
9 dh_auto_build --buildsystem=golang
10
8override_dh_install:11override_dh_install:
9 dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing12 dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing
1013
1114
=== modified file 'dependencies.tsv'
--- dependencies.tsv 2014-03-12 13:23:26 +0000
+++ dependencies.tsv 2014-04-29 18:02:00 +0000
@@ -2,3 +2,5 @@
2launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140206110213-pbzcr6ucaz3rqmnw 1252launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140206110213-pbzcr6ucaz3rqmnw 125
3launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 103launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10
4launchpad.net/gocheck bzr gustavo@niemeyer.net-20140127131816-zshobk1qqme626xw 864launchpad.net/gocheck bzr gustavo@niemeyer.net-20140127131816-zshobk1qqme626xw 86
5gopkg.in/qml.v0 git master 8adbc8c2bf2da9f609df366683ad0f47a89c3d49
6gopkg.in/niemeyer/uoneauth.v1 git v1 0758ba882a143ad2862dbcac85a7ca145750b640
57
=== added file 'externals.tgz'
6Binary files externals.tgz 1970-01-01 00:00:00 +0000 and externals.tgz 2014-04-29 18:02:00 +0000 differ8Binary files externals.tgz 1970-01-01 00:00:00 +0000 and externals.tgz 2014-04-29 18:02:00 +0000 differ
=== modified file 'logger/logger.go'
--- logger/logger.go 2014-02-24 10:27:38 +0000
+++ logger/logger.go 2014-04-29 18:02:00 +0000
@@ -23,6 +23,8 @@
23 "log"23 "log"
24 "os"24 "os"
25 "runtime"25 "runtime"
26
27 "launchpad.net/ubuntu-push/config"
26)28)
2729
28// Logger is a simple logger interface with logging at levels.30// Logger is a simple logger interface with logging at levels.
@@ -119,3 +121,28 @@
119 lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...))121 lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...))
120 }122 }
121}123}
124
125// config bits
126
127// ConfigLogLevel can hold a log level in a configuration struct.
128type ConfigLogLevel string
129
130func (cll *ConfigLogLevel) ConfigFromJSONString() {}
131
132func (cll *ConfigLogLevel) UnmarshalJSON(b []byte) error {
133 return config.UnmarshalJSONViaString(cll, b)
134}
135
136func (cll *ConfigLogLevel) SetFromString(enc string) error {
137 _, ok := levelToNLevel[enc]
138 if !ok {
139 return fmt.Errorf("not a log level: %s", enc)
140 }
141 *cll = ConfigLogLevel(enc)
142 return nil
143}
144
145// Level returns the log level string held in cll.
146func (cll ConfigLogLevel) Level() string {
147 return string(cll)
148}
122149
=== modified file 'logger/logger_test.go'
--- logger/logger_test.go 2014-02-10 22:51:43 +0000
+++ logger/logger_test.go 2014-04-29 18:02:00 +0000
@@ -25,6 +25,8 @@
25 "testing"25 "testing"
2626
27 . "launchpad.net/gocheck"27 . "launchpad.net/gocheck"
28
29 "launchpad.net/ubuntu-push/config"
28)30)
2931
30func TestLogger(t *testing.T) { TestingT(t) }32func TestLogger(t *testing.T) { TestingT(t) }
@@ -138,3 +140,26 @@
138 logger.Output(1, "foobaz")140 logger.Output(1, "foobaz")
139 c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n")141 c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n")
140}142}
143
144type testLogLevelConfig struct {
145 Lvl ConfigLogLevel
146}
147
148func (s *loggerSuite) TestReadConfigLogLevel(c *C) {
149 buf := bytes.NewBufferString(`{"lvl": "debug"}`)
150 var cfg testLogLevelConfig
151 err := config.ReadConfig(buf, &cfg)
152 c.Assert(err, IsNil)
153 c.Check(cfg.Lvl.Level(), Equals, "debug")
154}
155
156func (s *loggerSuite) TestReadConfigLogLevelErrors(c *C) {
157 var cfg testLogLevelConfig
158 checkError := func(jsonCfg string, expectedError string) {
159 buf := bytes.NewBufferString(jsonCfg)
160 err := config.ReadConfig(buf, &cfg)
161 c.Check(err, ErrorMatches, expectedError)
162 }
163 checkError(`{"lvl": 1}`, "lvl:.*type string")
164 checkError(`{"lvl": "foo"}`, "lvl: not a log level: foo")
165}
141166
=== modified file 'protocol/messages.go'
--- protocol/messages.go 2014-04-04 13:54:45 +0000
+++ protocol/messages.go 2014-04-29 18:02:00 +0000
@@ -54,6 +54,14 @@
54 Split() (done bool)54 Split() (done bool)
55}55}
5656
57// OnewayMsg are messages that are not to be followed by a response,
58// after sending them the session either aborts or continues.
59type OnewayMsg interface {
60 SplittableMsg
61 // continue session after the message?
62 OnewayContinue() bool
63}
64
57// CONNBROKEN message, server side is breaking the connection for reason.65// CONNBROKEN message, server side is breaking the connection for reason.
58type ConnBrokenMsg struct {66type ConnBrokenMsg struct {
59 Type string `json:"T"`67 Type string `json:"T"`
@@ -65,11 +73,35 @@
65 return true73 return true
66}74}
6775
76func (m *ConnBrokenMsg) OnewayContinue() bool {
77 return false
78}
79
68// CONNBROKEN reasons80// CONNBROKEN reasons
69const (81const (
70 BrokenHostMismatch = "host-mismatch"82 BrokenHostMismatch = "host-mismatch"
71)83)
7284
85// CONNWARN message, server side is warning about partial functionality
86// because reason.
87type ConnWarnMsg struct {
88 Type string `json:"T"`
89 // reason
90 Reason string
91}
92
93func (m *ConnWarnMsg) Split() bool {
94 return true
95}
96func (m *ConnWarnMsg) OnewayContinue() bool {
97 return true
98}
99
100// CONNWARN reasons
101const (
102 WarnUnauthorized = "unauthorized"
103)
104
73// PING/PONG messages105// PING/PONG messages
74type PingPongMsg struct {106type PingPongMsg struct {
75 Type string `json:"T"`107 Type string `json:"T"`
@@ -130,6 +162,19 @@
130 Payload json.RawMessage `json:"P"`162 Payload json.RawMessage `json:"P"`
131}163}
132164
165// ExtractPayloads gets only the payloads out of a slice of notications.
166func ExtractPayloads(notifications []Notification) []json.RawMessage {
167 n := len(notifications)
168 if n == 0 {
169 return nil
170 }
171 payloads := make([]json.RawMessage, n)
172 for i := 0; i < n; i++ {
173 payloads[i] = notifications[i].Payload
174 }
175 return payloads
176}
177
133// ACKnowledgement message178// ACKnowledgement message
134type AckMsg struct {179type AckMsg struct {
135 Type string `json:"T"`180 Type string `json:"T"`
136181
=== modified file 'protocol/messages_test.go'
--- protocol/messages_test.go 2014-04-04 13:19:10 +0000
+++ protocol/messages_test.go 2014-04-29 18:02:00 +0000
@@ -104,6 +104,22 @@
104 c.Check(b.splitting, Equals, 0)104 c.Check(b.splitting, Equals, 0)
105}105}
106106
107func (s *messagesSuite) TestSplitConnBrokenMsg(c *C) {107func (s *messagesSuite) TestConnBrokenMsg(c *C) {
108 c.Check((&ConnBrokenMsg{}).Split(), Equals, true)108 m := &ConnBrokenMsg{}
109 c.Check(m.Split(), Equals, true)
110 c.Check(m.OnewayContinue(), Equals, false)
111}
112
113func (s *messagesSuite) TestConnWarnMsg(c *C) {
114 m := &ConnWarnMsg{}
115 c.Check(m.Split(), Equals, true)
116 c.Check(m.OnewayContinue(), Equals, true)
117}
118
119func (s *messagesSuite) TestExtractPayloads(c *C) {
120 c.Check(ExtractPayloads(nil), IsNil)
121 p1 := json.RawMessage(`{"a":1}`)
122 p2 := json.RawMessage(`{"b":2}`)
123 ns := []Notification{Notification{Payload: p1}, Notification{Payload: p2}}
124 c.Check(ExtractPayloads(ns), DeepEquals, []json.RawMessage{p1, p2})
109}125}
110126
=== modified file 'protocol/state-diag-client.gv'
--- protocol/state-diag-client.gv 2014-01-16 20:07:13 +0000
+++ protocol/state-diag-client.gv 2014-04-29 18:02:00 +0000
@@ -2,7 +2,7 @@
2 label = "State diagram for client";2 label = "State diagram for client";
3 size="12,6";3 size="12,6";
4 rankdir=LR;4 rankdir=LR;
5 node [shape = doublecircle]; pingTimeout;5 node [shape = doublecircle]; pingTimeout; connBroken;
6 node [shape = circle];6 node [shape = circle];
7 start1 -> start2 [ label = "Write wire version" ];7 start1 -> start2 [ label = "Write wire version" ];
8 start2 -> start3 [ label = "Write CONNECT" ];8 start2 -> start3 [ label = "Write CONNECT" ];
@@ -13,4 +13,7 @@
13 broadcast -> loop [label = "Write ACK"];13 broadcast -> loop [label = "Write ACK"];
14 loop -> pingTimeout [14 loop -> pingTimeout [
15 label = "Elapsed ping interval + exchange interval"];15 label = "Elapsed ping interval + exchange interval"];
16 loop -> connBroken [label = "Read CONNBROKEN"];
17 loop -> warn [label = "Read CONNWARN"];
18 warn -> loop;
16}19}
1720
=== modified file 'protocol/state-diag-client.svg'
--- protocol/state-diag-client.svg 2014-01-16 19:37:57 +0000
+++ protocol/state-diag-client.svg 2014-04-29 18:02:00 +0000
@@ -4,95 +4,123 @@
4<!-- Generated by graphviz version 2.26.3 (20100126.1600)4<!-- Generated by graphviz version 2.26.3 (20100126.1600)
5 -->5 -->
6<!-- Title: state_diagram_client Pages: 1 -->6<!-- Title: state_diagram_client Pages: 1 -->
7<svg width="864pt" height="279pt"7<svg width="822pt" height="432pt"
8 viewBox="0.00 0.00 864.00 278.89" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">8 viewBox="0.00 0.00 822.36 432.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9<g id="graph1" class="graph" transform="scale(0.683544 0.683544) rotate(0) translate(4 404)">9<g id="graph1" class="graph" transform="scale(0.650602 0.650602) rotate(0) translate(4 660)">
10<title>state_diagram_client</title>10<title>state_diagram_client</title>
11<polygon fill="white" stroke="white" points="-4,5 -4,-404 1261,-404 1261,5 -4,5"/>11<polygon fill="white" stroke="white" points="-4,5 -4,-660 1261,-660 1261,5 -4,5"/>
12<text text-anchor="middle" x="628" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for client</text>12<text text-anchor="middle" x="628" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for client</text>
13<!-- pingTimeout -->13<!-- pingTimeout -->
14<g id="node1" class="node"><title>pingTimeout</title>14<g id="node1" class="node"><title>pingTimeout</title>
15<ellipse fill="none" stroke="black" cx="1180" cy="-324" rx="72.1249" ry="72.1249"/>15<ellipse fill="none" stroke="black" cx="1180" cy="-580" rx="72.1249" ry="72.1249"/>
16<ellipse fill="none" stroke="black" cx="1180" cy="-324" rx="76.1249" ry="76.1249"/>16<ellipse fill="none" stroke="black" cx="1180" cy="-580" rx="76.1249" ry="76.1249"/>
17<text text-anchor="middle" x="1180" y="-320.4" font-family="Times Roman,serif" font-size="14.00">pingTimeout</text>17<text text-anchor="middle" x="1180" y="-576.4" font-family="Times Roman,serif" font-size="14.00">pingTimeout</text>
18</g>
19<!-- connBroken -->
20<g id="node2" class="node"><title>connBroken</title>
21<ellipse fill="none" stroke="black" cx="1180" cy="-413" rx="68.8251" ry="69.2965"/>
22<ellipse fill="none" stroke="black" cx="1180" cy="-413" rx="72.7978" ry="73.2965"/>
23<text text-anchor="middle" x="1180" y="-409.4" font-family="Times Roman,serif" font-size="14.00">connBroken</text>
18</g>24</g>
19<!-- start1 -->25<!-- start1 -->
20<g id="node2" class="node"><title>start1</title>26<g id="node3" class="node"><title>start1</title>
21<ellipse fill="none" stroke="black" cx="42" cy="-166" rx="41.2167" ry="41.7193"/>27<ellipse fill="none" stroke="black" cx="42" cy="-231" rx="41.2167" ry="41.7193"/>
22<text text-anchor="middle" x="42" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start1</text>28<text text-anchor="middle" x="42" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start1</text>
23</g>29</g>
24<!-- start2 -->30<!-- start2 -->
25<g id="node4" class="node"><title>start2</title>31<g id="node5" class="node"><title>start2</title>
26<ellipse fill="none" stroke="black" cx="292" cy="-166" rx="41.2167" ry="41.7193"/>32<ellipse fill="none" stroke="black" cx="292" cy="-231" rx="41.2167" ry="41.7193"/>
27<text text-anchor="middle" x="292" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start2</text>33<text text-anchor="middle" x="292" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start2</text>
28</g>34</g>
29<!-- start1&#45;&gt;start2 -->35<!-- start1&#45;&gt;start2 -->
30<g id="edge2" class="edge"><title>start1&#45;&gt;start2</title>36<g id="edge2" class="edge"><title>start1&#45;&gt;start2</title>
31<path fill="none" stroke="black" d="M83.5631,-166C126.547,-166 193.757,-166 240.181,-166"/>37<path fill="none" stroke="black" d="M83.5631,-231C126.547,-231 193.757,-231 240.181,-231"/>
32<polygon fill="black" stroke="black" points="240.338,-169.5 250.338,-166 240.338,-162.5 240.338,-169.5"/>38<polygon fill="black" stroke="black" points="240.338,-234.5 250.338,-231 240.338,-227.5 240.338,-234.5"/>
33<text text-anchor="middle" x="167" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Write wire version</text>39<text text-anchor="middle" x="167" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Write wire version</text>
34</g>40</g>
35<!-- start3 -->41<!-- start3 -->
36<g id="node6" class="node"><title>start3</title>42<g id="node7" class="node"><title>start3</title>
37<ellipse fill="none" stroke="black" cx="526" cy="-166" rx="41.2167" ry="41.7193"/>43<ellipse fill="none" stroke="black" cx="526" cy="-231" rx="41.2167" ry="41.7193"/>
38<text text-anchor="middle" x="526" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start3</text>44<text text-anchor="middle" x="526" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start3</text>
39</g>45</g>
40<!-- start2&#45;&gt;start3 -->46<!-- start2&#45;&gt;start3 -->
41<g id="edge4" class="edge"><title>start2&#45;&gt;start3</title>47<g id="edge4" class="edge"><title>start2&#45;&gt;start3</title>
42<path fill="none" stroke="black" d="M333.565,-166C372.875,-166 431.992,-166 474.321,-166"/>48<path fill="none" stroke="black" d="M333.565,-231C372.875,-231 431.992,-231 474.321,-231"/>
43<polygon fill="black" stroke="black" points="474.429,-169.5 484.429,-166 474.429,-162.5 474.429,-169.5"/>49<polygon fill="black" stroke="black" points="474.429,-234.5 484.429,-231 474.429,-227.5 474.429,-234.5"/>
44<text text-anchor="middle" x="409" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Write CONNECT</text>50<text text-anchor="middle" x="409" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Write CONNECT</text>
45</g>51</g>
46<!-- loop -->52<!-- loop -->
47<g id="node8" class="node"><title>loop</title>53<g id="node9" class="node"><title>loop</title>
48<ellipse fill="none" stroke="black" cx="746" cy="-166" rx="31.8198" ry="31.8198"/>54<ellipse fill="none" stroke="black" cx="746" cy="-231" rx="31.8198" ry="31.8198"/>
49<text text-anchor="middle" x="746" y="-162.4" font-family="Times Roman,serif" font-size="14.00">loop</text>55<text text-anchor="middle" x="746" y="-227.4" font-family="Times Roman,serif" font-size="14.00">loop</text>
50</g>56</g>
51<!-- start3&#45;&gt;loop -->57<!-- start3&#45;&gt;loop -->
52<g id="edge6" class="edge"><title>start3&#45;&gt;loop</title>58<g id="edge6" class="edge"><title>start3&#45;&gt;loop</title>
53<path fill="none" stroke="black" d="M567.639,-166C606.633,-166 664.616,-166 703.793,-166"/>59<path fill="none" stroke="black" d="M567.639,-231C606.633,-231 664.616,-231 703.793,-231"/>
54<polygon fill="black" stroke="black" points="703.818,-169.5 713.818,-166 703.818,-162.5 703.818,-169.5"/>60<polygon fill="black" stroke="black" points="703.818,-234.5 713.818,-231 703.818,-227.5 703.818,-234.5"/>
55<text text-anchor="middle" x="641" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Read CONNACK</text>61<text text-anchor="middle" x="641" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Read CONNACK</text>
56</g>62</g>
57<!-- loop&#45;&gt;pingTimeout -->63<!-- loop&#45;&gt;pingTimeout -->
58<g id="edge16" class="edge"><title>loop&#45;&gt;pingTimeout</title>64<g id="edge16" class="edge"><title>loop&#45;&gt;pingTimeout</title>
59<path fill="none" stroke="black" d="M763.666,-192.937C772.211,-204.042 783.361,-216.128 796,-224 888.06,-281.339 1012.12,-305.973 1094,-316.443"/>65<path fill="none" stroke="black" d="M750.211,-262.971C757.458,-313.528 773.689,-408.79 796,-434 872.806,-520.784 1006.81,-556.22 1094.46,-570.528"/>
60<polygon fill="black" stroke="black" points="1093.67,-319.928 1104.02,-317.68 1094.53,-312.981 1093.67,-319.928"/>66<polygon fill="black" stroke="black" points="1093.96,-573.992 1104.39,-572.09 1095.05,-567.078 1093.96,-573.992"/>
61<text text-anchor="middle" x="941" y="-319.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval + exchange interval</text>67<text text-anchor="middle" x="941" y="-572.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval + exchange interval</text>
68</g>
69<!-- loop&#45;&gt;connBroken -->
70<g id="edge18" class="edge"><title>loop&#45;&gt;connBroken</title>
71<path fill="none" stroke="black" d="M755.1,-261.824C762.755,-282.438 775.756,-308.526 796,-324 883.382,-390.791 1012.39,-408.797 1096.33,-412.948"/>
72<polygon fill="black" stroke="black" points="1096.19,-416.445 1106.33,-413.388 1096.5,-409.452 1096.19,-416.445"/>
73<text text-anchor="middle" x="941" y="-417.4" font-family="Times Roman,serif" font-size="14.00">Read CONNBROKEN</text>
62</g>74</g>
63<!-- pong -->75<!-- pong -->
64<g id="node10" class="node"><title>pong</title>76<g id="node11" class="node"><title>pong</title>
65<ellipse fill="none" stroke="black" cx="1180" cy="-195" rx="34.8574" ry="35.3553"/>77<ellipse fill="none" stroke="black" cx="1180" cy="-287" rx="34.8574" ry="35.3553"/>
66<text text-anchor="middle" x="1180" y="-191.4" font-family="Times Roman,serif" font-size="14.00">pong</text>78<text text-anchor="middle" x="1180" y="-283.4" font-family="Times Roman,serif" font-size="14.00">pong</text>
67</g>79</g>
68<!-- loop&#45;&gt;pong -->80<!-- loop&#45;&gt;pong -->
69<g id="edge8" class="edge"><title>loop&#45;&gt;pong</title>81<g id="edge8" class="edge"><title>loop&#45;&gt;pong</title>
70<path fill="none" stroke="black" d="M775.392,-179.044C782.046,-181.465 789.167,-183.653 796,-185 916.362,-208.722 1062.02,-203.515 1134.48,-198.706"/>82<path fill="none" stroke="black" d="M768.467,-253.959C776.476,-260.698 786.005,-267.259 796,-271 911.696,-314.31 1060.9,-303.343 1134.62,-293.955"/>
71<polygon fill="black" stroke="black" points="1134.89,-202.186 1144.62,-198.003 1134.4,-195.203 1134.89,-202.186"/>83<polygon fill="black" stroke="black" points="1135.49,-297.371 1144.94,-292.588 1134.57,-290.432 1135.49,-297.371"/>
72<text text-anchor="middle" x="941" y="-207.4" font-family="Times Roman,serif" font-size="14.00">Read PING</text>84<text text-anchor="middle" x="941" y="-307.4" font-family="Times Roman,serif" font-size="14.00">Read PING</text>
73</g>85</g>
74<!-- broadcast -->86<!-- broadcast -->
75<g id="node12" class="node"><title>broadcast</title>87<g id="node13" class="node"><title>broadcast</title>
76<ellipse fill="none" stroke="black" cx="1180" cy="-84" rx="58.1882" ry="58.6899"/>88<ellipse fill="none" stroke="black" cx="1180" cy="-176" rx="58.1882" ry="58.6899"/>
77<text text-anchor="middle" x="1180" y="-80.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text>89<text text-anchor="middle" x="1180" y="-172.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text>
78</g>90</g>
79<!-- loop&#45;&gt;broadcast -->91<!-- loop&#45;&gt;broadcast -->
80<g id="edge10" class="edge"><title>loop&#45;&gt;broadcast</title>92<g id="edge10" class="edge"><title>loop&#45;&gt;broadcast</title>
81<path fill="none" stroke="black" d="M770.52,-145.1C778.217,-139.607 787.053,-134.301 796,-131 917.482,-86.1746 957.924,-122.075 1086,-103 1094.61,-101.717 1103.63,-100.165 1112.53,-98.5074"/>93<path fill="none" stroke="black" d="M775.45,-218.228C782.1,-215.791 789.205,-213.528 796,-212 922.145,-183.64 957.464,-202.973 1086,-189 1094.36,-188.091 1103.12,-187.028 1111.79,-185.909"/>
82<polygon fill="black" stroke="black" points="1113.34,-101.917 1122.5,-96.5998 1112.02,-95.0419 1113.34,-101.917"/>94<polygon fill="black" stroke="black" points="1112.44,-189.353 1121.9,-184.574 1111.53,-182.413 1112.44,-189.353"/>
83<text text-anchor="middle" x="941" y="-136.4" font-family="Times Roman,serif" font-size="14.00">Read BROADCAST</text>95<text text-anchor="middle" x="941" y="-217.4" font-family="Times Roman,serif" font-size="14.00">Read BROADCAST</text>
96</g>
97<!-- warn -->
98<g id="node19" class="node"><title>warn</title>
99<ellipse fill="none" stroke="black" cx="1180" cy="-63" rx="36.7696" ry="36.7696"/>
100<text text-anchor="middle" x="1180" y="-59.4" font-family="Times Roman,serif" font-size="14.00">warn</text>
101</g>
102<!-- loop&#45;&gt;warn -->
103<g id="edge20" class="edge"><title>loop&#45;&gt;warn</title>
104<path fill="none" stroke="black" d="M753.357,-199.767C760.401,-177.027 773.396,-147.441 796,-131 901.425,-54.3166 958.242,-112.935 1086,-87 1101.84,-83.7841 1119.02,-79.6061 1134.3,-75.6396"/>
105<polygon fill="black" stroke="black" points="1135.26,-79.0068 1144.04,-73.0757 1133.48,-72.2376 1135.26,-79.0068"/>
106<text text-anchor="middle" x="941" y="-136.4" font-family="Times Roman,serif" font-size="14.00">Read CONNWARN</text>
84</g>107</g>
85<!-- pong&#45;&gt;loop -->108<!-- pong&#45;&gt;loop -->
86<g id="edge12" class="edge"><title>pong&#45;&gt;loop</title>109<g id="edge12" class="edge"><title>pong&#45;&gt;loop</title>
87<path fill="none" stroke="black" d="M1147.19,-180.867C1129.44,-173.986 1106.92,-166.463 1086,-163 980.081,-145.465 853.051,-154.36 788.368,-160.981"/>110<path fill="none" stroke="black" d="M1148.22,-271.079C1130.39,-262.942 1107.48,-253.77 1086,-249 1030.54,-236.684 866.695,-232.715 788.482,-231.502"/>
88<polygon fill="black" stroke="black" points="787.736,-157.528 778.16,-162.06 788.472,-164.489 787.736,-157.528"/>111<polygon fill="black" stroke="black" points="788.085,-227.996 778.035,-231.348 787.982,-234.995 788.085,-227.996"/>
89<text text-anchor="middle" x="941" y="-168.4" font-family="Times Roman,serif" font-size="14.00">Write PONG</text>112<text text-anchor="middle" x="941" y="-254.4" font-family="Times Roman,serif" font-size="14.00">Write PONG</text>
90</g>113</g>
91<!-- broadcast&#45;&gt;loop -->114<!-- broadcast&#45;&gt;loop -->
92<g id="edge14" class="edge"><title>broadcast&#45;&gt;loop</title>115<g id="edge14" class="edge"><title>broadcast&#45;&gt;loop</title>
93<path fill="none" stroke="black" d="M1123.8,-67.0114C1044.83,-46.6166 899.156,-22.0001 796,-81 778.946,-90.7538 767.135,-108.842 759.293,-125.833"/>116<path fill="none" stroke="black" d="M1121.7,-168.205C1028.72,-156.837 851.665,-139.849 796,-167 784,-172.853 774.037,-183.132 766.245,-193.762"/>
94<polygon fill="black" stroke="black" points="756.044,-124.528 755.336,-135.099 762.482,-127.277 756.044,-124.528"/>117<polygon fill="black" stroke="black" points="763.182,-192.043 760.465,-202.284 768.975,-195.973 763.182,-192.043"/>
95<text text-anchor="middle" x="941" y="-86.4" font-family="Times Roman,serif" font-size="14.00">Write ACK</text>118<text text-anchor="middle" x="941" y="-172.4" font-family="Times Roman,serif" font-size="14.00">Write ACK</text>
119</g>
120<!-- warn&#45;&gt;loop -->
121<g id="edge22" class="edge"><title>warn&#45;&gt;loop</title>
122<path fill="none" stroke="black" d="M1144.07,-53.3553C1070.4,-35.8873 900.397,-7.71825 796,-87 779.313,-99.6722 764.14,-151.763 754.991,-189.659"/>
123<polygon fill="black" stroke="black" points="751.574,-188.904 752.686,-199.44 758.387,-190.51 751.574,-188.904"/>
96</g>124</g>
97</g>125</g>
98</svg>126</svg>
99127
=== modified file 'protocol/state-diag-session.gv'
--- protocol/state-diag-session.gv 2014-01-16 20:07:13 +0000
+++ protocol/state-diag-session.gv 2014-04-29 18:02:00 +0000
@@ -2,6 +2,7 @@
2 label = "State diagram for session";2 label = "State diagram for session";
3 size="12,6";3 size="12,6";
4 rankdir=LR;4 rankdir=LR;
5 node [shape = doublecircle]; stop;
5 node [shape = circle];6 node [shape = circle];
6 start1 -> start2 [ label = "Read wire version" ];7 start1 -> start2 [ label = "Read wire version" ];
7 start2 -> start3 [ label = "Read CONNECT" ];8 start2 -> start3 [ label = "Read CONNECT" ];
@@ -17,4 +18,13 @@
17 split_broadcast -> split_ack_wait [label = "Write split BROADCAST"];18 split_broadcast -> split_ack_wait [label = "Write split BROADCAST"];
18 split_ack_wait -> split_broadcast [label = "Read ACK"];19 split_ack_wait -> split_broadcast [label = "Read ACK"];
19 split_broadcast -> loop [label = "All split msgs written"];20 split_broadcast -> loop [label = "All split msgs written"];
21 // other
22 loop -> conn_broken [label = "Receive connbroken request"];
23 loop -> conn_warn [label = "Receive connwarn request"];
24 conn_broken -> stop [label = "Write CONNBROKEN"];
25 conn_warn -> loop [label = "Write CONNWARN"];
26 // timeouts
27 ack_wait -> stop [label = "Elapsed exhange timeout"];
28 split_ack_wait -> stop [label = "Elapsed exhange timeout"];
29 pong_wait -> stop [label = "Elapsed exhange timeout"];
20}30}
2131
=== modified file 'protocol/state-diag-session.svg'
--- protocol/state-diag-session.svg 2014-01-16 19:37:57 +0000
+++ protocol/state-diag-session.svg 2014-04-29 18:02:00 +0000
@@ -4,139 +4,197 @@
4<!-- Generated by graphviz version 2.26.3 (20100126.1600)4<!-- Generated by graphviz version 2.26.3 (20100126.1600)
5 -->5 -->
6<!-- Title: state_diagram_session Pages: 1 -->6<!-- Title: state_diagram_session Pages: 1 -->
7<svg width="864pt" height="208pt"7<svg width="864pt" height="266pt"
8 viewBox="0.00 0.00 864.00 207.94" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">8 viewBox="0.00 0.00 864.00 265.73" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9<g id="graph1" class="graph" transform="scale(0.435923 0.435923) rotate(0) translate(4 473)">9<g id="graph1" class="graph" transform="scale(0.367035 0.367035) rotate(0) translate(4 720)">
10<title>state_diagram_session</title>10<title>state_diagram_session</title>
11<polygon fill="white" stroke="white" points="-4,5 -4,-473 1979,-473 1979,5 -4,5"/>11<polygon fill="white" stroke="white" points="-4,5 -4,-720 2351,-720 2351,5 -4,5"/>
12<text text-anchor="middle" x="987" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for session</text>12<text text-anchor="middle" x="1173" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for session</text>
13<!-- stop -->
14<g id="node1" class="node"><title>stop</title>
15<ellipse fill="none" stroke="black" cx="2309" cy="-335" rx="32.0813" ry="32.5269"/>
16<ellipse fill="none" stroke="black" cx="2309" cy="-335" rx="36.0265" ry="36.5269"/>
17<text text-anchor="middle" x="2309" y="-331.4" font-family="Times Roman,serif" font-size="14.00">stop</text>
18</g>
13<!-- start1 -->19<!-- start1 -->
14<g id="node1" class="node"><title>start1</title>20<g id="node2" class="node"><title>start1</title>
15<ellipse fill="none" stroke="black" cx="42" cy="-294" rx="41.2167" ry="41.7193"/>21<ellipse fill="none" stroke="black" cx="42" cy="-395" rx="41.2167" ry="41.7193"/>
16<text text-anchor="middle" x="42" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start1</text>22<text text-anchor="middle" x="42" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start1</text>
17</g>23</g>
18<!-- start2 -->24<!-- start2 -->
19<g id="node3" class="node"><title>start2</title>25<g id="node4" class="node"><title>start2</title>
20<ellipse fill="none" stroke="black" cx="286" cy="-294" rx="41.2167" ry="41.7193"/>26<ellipse fill="none" stroke="black" cx="286" cy="-395" rx="41.2167" ry="41.7193"/>
21<text text-anchor="middle" x="286" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start2</text>27<text text-anchor="middle" x="286" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start2</text>
22</g>28</g>
23<!-- start1&#45;&gt;start2 -->29<!-- start1&#45;&gt;start2 -->
24<g id="edge2" class="edge"><title>start1&#45;&gt;start2</title>30<g id="edge2" class="edge"><title>start1&#45;&gt;start2</title>
25<path fill="none" stroke="black" d="M83.6679,-294C125.213,-294 189.13,-294 233.981,-294"/>31<path fill="none" stroke="black" d="M83.6679,-395C125.213,-395 189.13,-395 233.981,-395"/>
26<polygon fill="black" stroke="black" points="234.096,-297.5 244.096,-294 234.096,-290.5 234.096,-297.5"/>32<polygon fill="black" stroke="black" points="234.096,-398.5 244.096,-395 234.096,-391.5 234.096,-398.5"/>
27<text text-anchor="middle" x="164" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Read wire version</text>33<text text-anchor="middle" x="164" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Read wire version</text>
28</g>34</g>
29<!-- start3 -->35<!-- start3 -->
30<g id="node5" class="node"><title>start3</title>36<g id="node6" class="node"><title>start3</title>
31<ellipse fill="none" stroke="black" cx="516" cy="-294" rx="41.2167" ry="41.7193"/>37<ellipse fill="none" stroke="black" cx="537" cy="-395" rx="41.2167" ry="41.7193"/>
32<text text-anchor="middle" x="516" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start3</text>38<text text-anchor="middle" x="537" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start3</text>
33</g>39</g>
34<!-- start2&#45;&gt;start3 -->40<!-- start2&#45;&gt;start3 -->
35<g id="edge4" class="edge"><title>start2&#45;&gt;start3</title>41<g id="edge4" class="edge"><title>start2&#45;&gt;start3</title>
36<path fill="none" stroke="black" d="M327.651,-294C365.959,-294 422.903,-294 464.145,-294"/>42<path fill="none" stroke="black" d="M327.729,-395C370.886,-395 438.364,-395 484.973,-395"/>
37<polygon fill="black" stroke="black" points="464.271,-297.5 474.271,-294 464.271,-290.5 464.271,-297.5"/>43<polygon fill="black" stroke="black" points="485.171,-398.5 495.171,-395 485.171,-391.5 485.171,-398.5"/>
38<text text-anchor="middle" x="401" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Read CONNECT</text>44<text text-anchor="middle" x="401" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Read CONNECT</text>
39</g>45</g>
40<!-- loop -->46<!-- loop -->
41<g id="node7" class="node"><title>loop</title>47<g id="node8" class="node"><title>loop</title>
42<ellipse fill="none" stroke="black" cx="740" cy="-294" rx="31.8198" ry="31.8198"/>48<ellipse fill="none" stroke="black" cx="790" cy="-395" rx="31.8198" ry="31.8198"/>
43<text text-anchor="middle" x="740" y="-290.4" font-family="Times Roman,serif" font-size="14.00">loop</text>49<text text-anchor="middle" x="790" y="-391.4" font-family="Times Roman,serif" font-size="14.00">loop</text>
44</g>50</g>
45<!-- start3&#45;&gt;loop -->51<!-- start3&#45;&gt;loop -->
46<g id="edge6" class="edge"><title>start3&#45;&gt;loop</title>52<g id="edge6" class="edge"><title>start3&#45;&gt;loop</title>
47<path fill="none" stroke="black" d="M557.608,-294C597.53,-294 657.517,-294 697.677,-294"/>53<path fill="none" stroke="black" d="M578.778,-395C625.49,-395 700.728,-395 747.665,-395"/>
48<polygon fill="black" stroke="black" points="697.687,-297.5 707.687,-294 697.687,-290.5 697.687,-297.5"/>54<polygon fill="black" stroke="black" points="747.805,-398.5 757.805,-395 747.805,-391.5 747.805,-398.5"/>
49<text text-anchor="middle" x="633" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Write CONNACK</text>55<text text-anchor="middle" x="675" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Write CONNACK</text>
50</g>56</g>
51<!-- ping -->57<!-- ping -->
52<g id="node9" class="node"><title>ping</title>58<g id="node10" class="node"><title>ping</title>
53<ellipse fill="none" stroke="black" cx="1063" cy="-416" rx="32.0265" ry="32.5269"/>59<ellipse fill="none" stroke="black" cx="1135" cy="-593" rx="32.0265" ry="32.5269"/>
54<text text-anchor="middle" x="1063" y="-412.4" font-family="Times Roman,serif" font-size="14.00">ping</text>60<text text-anchor="middle" x="1135" y="-589.4" font-family="Times Roman,serif" font-size="14.00">ping</text>
55</g>61</g>
56<!-- loop&#45;&gt;ping -->62<!-- loop&#45;&gt;ping -->
57<g id="edge8" class="edge"><title>loop&#45;&gt;ping</title>63<g id="edge8" class="edge"><title>loop&#45;&gt;ping</title>
58<path fill="none" stroke="black" d="M754.564,-322.853C763.046,-336.78 775.035,-352.491 790,-362 861.597,-407.491 963.396,-415.983 1020.29,-416.829"/>64<path fill="none" stroke="black" d="M800.39,-425.317C809.609,-448.006 825.187,-478.237 848,-497 920.691,-556.785 1032.18,-579.907 1092.58,-588.403"/>
59<polygon fill="black" stroke="black" points="1020.35,-420.33 1030.38,-416.906 1020.4,-413.33 1020.35,-420.33"/>65<polygon fill="black" stroke="black" points="1092.15,-591.877 1102.53,-589.734 1093.08,-584.939 1092.15,-591.877"/>
60<text text-anchor="middle" x="881" y="-418.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval</text>66<text text-anchor="middle" x="946" y="-583.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval</text>
61</g>67</g>
62<!-- broadcast -->68<!-- broadcast -->
63<g id="node11" class="node"><title>broadcast</title>69<g id="node12" class="node"><title>broadcast</title>
64<ellipse fill="none" stroke="black" cx="1063" cy="-200" rx="58.1882" ry="58.6899"/>70<ellipse fill="none" stroke="black" cx="1135" cy="-281" rx="58.1882" ry="58.6899"/>
65<text text-anchor="middle" x="1063" y="-196.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text>71<text text-anchor="middle" x="1135" y="-277.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text>
66</g>72</g>
67<!-- loop&#45;&gt;broadcast -->73<!-- loop&#45;&gt;broadcast -->
68<g id="edge10" class="edge"><title>loop&#45;&gt;broadcast</title>74<g id="edge10" class="edge"><title>loop&#45;&gt;broadcast</title>
69<path fill="none" stroke="black" d="M766.046,-274.934C773.498,-270.155 781.824,-265.421 790,-262 856.828,-234.035 938.382,-217.617 994.86,-208.779"/>75<path fill="none" stroke="black" d="M811.332,-370.953C821.492,-360.892 834.388,-349.946 848,-343 917.32,-307.628 1006.03,-292.395 1066.35,-285.86"/>
70<polygon fill="black" stroke="black" points="995.396,-212.238 1004.75,-207.269 994.34,-205.318 995.396,-212.238"/>76<polygon fill="black" stroke="black" points="1066.94,-289.319 1076.53,-284.811 1066.22,-282.355 1066.94,-289.319"/>
71<text text-anchor="middle" x="881" y="-267.4" font-family="Times Roman,serif" font-size="14.00">Receive broadcast request</text>77<text text-anchor="middle" x="946" y="-348.4" font-family="Times Roman,serif" font-size="14.00">Receive broadcast request</text>
78</g>
79<!-- conn_broken -->
80<g id="node26" class="node"><title>conn_broken</title>
81<ellipse fill="none" stroke="black" cx="1361" cy="-99" rx="73.0388" ry="73.5391"/>
82<text text-anchor="middle" x="1361" y="-95.4" font-family="Times Roman,serif" font-size="14.00">conn_broken</text>
83</g>
84<!-- loop&#45;&gt;conn_broken -->
85<g id="edge28" class="edge"><title>loop&#45;&gt;conn_broken</title>
86<path fill="none" stroke="black" d="M793.216,-363.054C799.833,-304.219 817.014,-182.243 848,-155 967.196,-50.2026 1167.08,-63.6291 1278.91,-81.8408"/>
87<polygon fill="black" stroke="black" points="1278.34,-85.2954 1288.79,-83.4998 1279.5,-78.392 1278.34,-85.2954"/>
88<text text-anchor="middle" x="946" y="-160.4" font-family="Times Roman,serif" font-size="14.00">Receive connbroken request</text>
89</g>
90<!-- conn_warn -->
91<g id="node28" class="node"><title>conn_warn</title>
92<ellipse fill="none" stroke="black" cx="1135" cy="-477" rx="65.7609" ry="65.7609"/>
93<text text-anchor="middle" x="1135" y="-473.4" font-family="Times Roman,serif" font-size="14.00">conn_warn</text>
94</g>
95<!-- loop&#45;&gt;conn_warn -->
96<g id="edge30" class="edge"><title>loop&#45;&gt;conn_warn</title>
97<path fill="none" stroke="black" d="M814.092,-416.512C823.957,-424.185 835.89,-432.126 848,-437 915.942,-464.343 999.421,-473.523 1058.8,-476.355"/>
98<polygon fill="black" stroke="black" points="1058.71,-479.855 1068.85,-476.786 1059.01,-472.861 1058.71,-479.855"/>
99<text text-anchor="middle" x="946" y="-480.4" font-family="Times Roman,serif" font-size="14.00">Receive connwarn request</text>
72</g>100</g>
73<!-- pong_wait -->101<!-- pong_wait -->
74<g id="node13" class="node"><title>pong_wait</title>102<g id="node14" class="node"><title>pong_wait</title>
75<ellipse fill="none" stroke="black" cx="1526" cy="-406" rx="62.9325" ry="62.9325"/>103<ellipse fill="none" stroke="black" cx="537" cy="-653" rx="62.9325" ry="62.9325"/>
76<text text-anchor="middle" x="1526" y="-402.4" font-family="Times Roman,serif" font-size="14.00">pong_wait</text>104<text text-anchor="middle" x="537" y="-649.4" font-family="Times Roman,serif" font-size="14.00">pong_wait</text>
77</g>105</g>
78<!-- ping&#45;&gt;pong_wait -->106<!-- ping&#45;&gt;pong_wait -->
79<g id="edge12" class="edge"><title>ping&#45;&gt;pong_wait</title>107<g id="edge12" class="edge"><title>ping&#45;&gt;pong_wait</title>
80<path fill="none" stroke="black" d="M1095.56,-415.297C1169.19,-413.707 1350.04,-409.8 1452.36,-407.591"/>108<path fill="none" stroke="black" d="M1103.4,-600.831C1035.81,-617.134 871.913,-654.261 732,-667 681.542,-671.594 668.481,-671.33 618,-667 615.134,-666.754 612.217,-666.46 609.275,-666.127"/>
81<polygon fill="black" stroke="black" points="1452.69,-411.084 1462.61,-407.369 1452.54,-404.086 1452.69,-411.084"/>109<polygon fill="black" stroke="black" points="609.573,-662.637 599.214,-664.858 608.697,-669.582 609.573,-662.637"/>
82<text text-anchor="middle" x="1289" y="-418.4" font-family="Times Roman,serif" font-size="14.00">Write PING</text>110<text text-anchor="middle" x="790" y="-670.4" font-family="Times Roman,serif" font-size="14.00">Write PING</text>
83</g>111</g>
84<!-- ack_wait -->112<!-- ack_wait -->
85<g id="node15" class="node"><title>ack_wait</title>113<g id="node16" class="node"><title>ack_wait</title>
86<ellipse fill="none" stroke="black" cx="1526" cy="-269" rx="55.1543" ry="55.1543"/>114<ellipse fill="none" stroke="black" cx="1598" cy="-373" rx="55.1543" ry="55.1543"/>
87<text text-anchor="middle" x="1526" y="-265.4" font-family="Times Roman,serif" font-size="14.00">ack_wait</text>115<text text-anchor="middle" x="1598" y="-369.4" font-family="Times Roman,serif" font-size="14.00">ack_wait</text>
88</g>116</g>
89<!-- broadcast&#45;&gt;ack_wait -->117<!-- broadcast&#45;&gt;ack_wait -->
90<g id="edge14" class="edge"><title>broadcast&#45;&gt;ack_wait</title>118<g id="edge14" class="edge"><title>broadcast&#45;&gt;ack_wait</title>
91<path fill="none" stroke="black" d="M1121.17,-208.669C1207.93,-221.599 1370.7,-245.856 1461.17,-259.339"/>119<path fill="none" stroke="black" d="M1193.25,-288.88C1264.95,-299.067 1390.23,-318.45 1496,-343 1508.9,-345.993 1522.57,-349.664 1535.58,-353.397"/>
92<polygon fill="black" stroke="black" points="1460.9,-262.837 1471.3,-260.849 1461.93,-255.913 1460.9,-262.837"/>120<polygon fill="black" stroke="black" points="1534.79,-356.813 1545.37,-356.254 1536.75,-350.093 1534.79,-356.813"/>
93<text text-anchor="middle" x="1289" y="-257.4" font-family="Times Roman,serif" font-size="14.00">Write BROADCAST [fits one wire msg]</text>121<text text-anchor="middle" x="1361" y="-348.4" font-family="Times Roman,serif" font-size="14.00">Write BROADCAST [fits one wire msg]</text>
94</g>122</g>
95<!-- split_broadcast -->123<!-- split_broadcast -->
96<g id="node17" class="node"><title>split_broadcast</title>124<g id="node18" class="node"><title>split_broadcast</title>
97<ellipse fill="none" stroke="black" cx="1526" cy="-110" rx="84.1457" ry="84.1457"/>125<ellipse fill="none" stroke="black" cx="1598" cy="-216" rx="84.1457" ry="84.1457"/>
98<text text-anchor="middle" x="1526" y="-106.4" font-family="Times Roman,serif" font-size="14.00">split_broadcast</text>126<text text-anchor="middle" x="1598" y="-212.4" font-family="Times Roman,serif" font-size="14.00">split_broadcast</text>
99</g>127</g>
100<!-- broadcast&#45;&gt;split_broadcast -->128<!-- broadcast&#45;&gt;split_broadcast -->
101<g id="edge16" class="edge"><title>broadcast&#45;&gt;split_broadcast</title>129<g id="edge16" class="edge"><title>broadcast&#45;&gt;split_broadcast</title>
102<path fill="none" stroke="black" d="M1120.7,-188.783C1199.06,-173.553 1340.01,-146.154 1433.29,-128.021"/>130<path fill="none" stroke="black" d="M1193.17,-272.834C1271.44,-261.846 1411.56,-242.174 1504.66,-229.104"/>
103<polygon fill="black" stroke="black" points="1434.15,-131.421 1443.29,-126.077 1432.81,-124.549 1434.15,-131.421"/>131<polygon fill="black" stroke="black" points="1505.23,-232.558 1514.65,-227.702 1504.26,-225.626 1505.23,-232.558"/>
104<text text-anchor="middle" x="1289" y="-185.4" font-family="Times Roman,serif" font-size="14.00">BROADCAST does not fit one wire msg</text>132<text text-anchor="middle" x="1361" y="-272.4" font-family="Times Roman,serif" font-size="14.00">BROADCAST does not fit one wire msg</text>
133</g>
134<!-- pong_wait&#45;&gt;stop -->
135<g id="edge40" class="edge"><title>pong_wait&#45;&gt;stop</title>
136<path fill="none" stroke="black" d="M600.164,-653C651.344,-653 725.322,-653 790,-653 790,-653 790,-653 1972,-653 2131.11,-653 2245.53,-463.168 2289.33,-376.844"/>
137<polygon fill="black" stroke="black" points="2292.5,-378.343 2293.84,-367.834 2286.24,-375.212 2292.5,-378.343"/>
138<text text-anchor="middle" x="1361" y="-658.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text>
105</g>139</g>
106<!-- pong_wait&#45;&gt;loop -->140<!-- pong_wait&#45;&gt;loop -->
107<g id="edge18" class="edge"><title>pong_wait&#45;&gt;loop</title>141<g id="edge18" class="edge"><title>pong_wait&#45;&gt;loop</title>
108<path fill="none" stroke="black" d="M1463.29,-398.59C1336,-383.27 1038.34,-346.004 790,-304 787.177,-303.523 784.269,-303.006 781.343,-302.468"/>142<path fill="none" stroke="black" d="M581.359,-607.765C632.774,-555.333 716.085,-470.376 760.273,-425.314"/>
109<polygon fill="black" stroke="black" points="781.898,-299.011 771.42,-300.582 780.59,-305.888 781.898,-299.011"/>143<polygon fill="black" stroke="black" points="762.774,-427.763 767.277,-418.172 757.776,-422.862 762.774,-427.763"/>
110<text text-anchor="middle" x="1063" y="-359.4" font-family="Times Roman,serif" font-size="14.00">Read PONG</text>144<text text-anchor="middle" x="675" y="-574.4" font-family="Times Roman,serif" font-size="14.00">Read PONG</text>
145</g>
146<!-- ack_wait&#45;&gt;stop -->
147<g id="edge36" class="edge"><title>ack_wait&#45;&gt;stop</title>
148<path fill="none" stroke="black" d="M1653.02,-372.757C1765.96,-371.776 2032.03,-366.986 2254,-344 2256.89,-343.701 2259.85,-343.348 2262.84,-342.959"/>
149<polygon fill="black" stroke="black" points="2263.58,-346.389 2272.99,-341.517 2262.59,-339.459 2263.58,-346.389"/>
150<text text-anchor="middle" x="1972" y="-373.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text>
111</g>151</g>
112<!-- ack_wait&#45;&gt;loop -->152<!-- ack_wait&#45;&gt;loop -->
113<g id="edge20" class="edge"><title>ack_wait&#45;&gt;loop</title>153<g id="edge20" class="edge"><title>ack_wait&#45;&gt;loop</title>
114<path fill="none" stroke="black" d="M1470.96,-271.898C1455.75,-272.644 1439.24,-273.404 1424,-274 1181.02,-283.507 889.323,-290.597 782.144,-293.057"/>154<path fill="none" stroke="black" d="M1542.83,-374.081C1445.66,-375.995 1237.64,-380.146 1062,-384 966.886,-386.087 942.947,-382.989 848,-389 842.829,-389.327 837.406,-389.764 832.04,-390.252"/>
115<polygon fill="black" stroke="black" points="781.977,-289.56 772.059,-293.288 782.136,-296.558 781.977,-289.56"/>155<polygon fill="black" stroke="black" points="831.485,-386.79 821.871,-391.242 832.163,-393.757 831.485,-386.79"/>
116<text text-anchor="middle" x="1063" y="-292.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text>156<text text-anchor="middle" x="1135" y="-389.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text>
117</g>157</g>
118<!-- split_broadcast&#45;&gt;loop -->158<!-- split_broadcast&#45;&gt;loop -->
119<g id="edge26" class="edge"><title>split_broadcast&#45;&gt;loop</title>159<g id="edge26" class="edge"><title>split_broadcast&#45;&gt;loop</title>
120<path fill="none" stroke="black" d="M1442.56,-99.9981C1336.06,-89.6718 1146.8,-79.5577 990,-115 894.383,-136.612 862.41,-141.921 790,-208 775.817,-220.943 764.522,-238.865 756.283,-254.999"/>160<path fill="none" stroke="black" d="M1515.33,-201.109C1409.22,-184.681 1219.98,-164.458 1062,-196 960.985,-216.168 924.079,-215.554 848,-285 827.351,-303.849 812.751,-331.776 803.364,-354.774"/>
121<polygon fill="black" stroke="black" points="753.014,-253.718 751.785,-264.241 759.308,-256.781 753.014,-253.718"/>161<polygon fill="black" stroke="black" points="800.027,-353.699 799.658,-364.287 806.549,-356.24 800.027,-353.699"/>
122<text text-anchor="middle" x="1063" y="-120.4" font-family="Times Roman,serif" font-size="14.00">All split msgs written</text>162<text text-anchor="middle" x="1135" y="-201.4" font-family="Times Roman,serif" font-size="14.00">All split msgs written</text>
123</g>163</g>
124<!-- split_ack_wait -->164<!-- split_ack_wait -->
125<g id="node21" class="node"><title>split_ack_wait</title>165<g id="node22" class="node"><title>split_ack_wait</title>
126<ellipse fill="none" stroke="black" cx="1893" cy="-110" rx="80.1095" ry="80.6102"/>166<ellipse fill="none" stroke="black" cx="1972" cy="-257" rx="80.1095" ry="80.6102"/>
127<text text-anchor="middle" x="1893" y="-106.4" font-family="Times Roman,serif" font-size="14.00">split_ack_wait</text>167<text text-anchor="middle" x="1972" y="-253.4" font-family="Times Roman,serif" font-size="14.00">split_ack_wait</text>
128</g>168</g>
129<!-- split_broadcast&#45;&gt;split_ack_wait -->169<!-- split_broadcast&#45;&gt;split_ack_wait -->
130<g id="edge22" class="edge"><title>split_broadcast&#45;&gt;split_ack_wait</title>170<g id="edge22" class="edge"><title>split_broadcast&#45;&gt;split_ack_wait</title>
131<path fill="none" stroke="black" d="M1610.2,-110C1667.61,-110 1743.59,-110 1802.33,-110"/>171<path fill="none" stroke="black" d="M1680.55,-232.126C1687.12,-233.18 1693.66,-234.156 1700,-235 1760.22,-243.022 1828.36,-248.528 1881.38,-252.025"/>
132<polygon fill="black" stroke="black" points="1802.35,-113.5 1812.35,-110 1802.34,-106.5 1802.35,-113.5"/>172<polygon fill="black" stroke="black" points="1881.23,-255.523 1891.43,-252.676 1881.68,-248.537 1881.23,-255.523"/>
133<text text-anchor="middle" x="1711" y="-115.4" font-family="Times Roman,serif" font-size="14.00">Write split BROADCAST</text>173<text text-anchor="middle" x="1783" y="-256.4" font-family="Times Roman,serif" font-size="14.00">Write split BROADCAST</text>
174</g>
175<!-- split_ack_wait&#45;&gt;stop -->
176<g id="edge38" class="edge"><title>split_ack_wait&#45;&gt;stop</title>
177<path fill="none" stroke="black" d="M2050.59,-275.189C2116.55,-290.456 2208.51,-311.74 2263.08,-324.372"/>
178<polygon fill="black" stroke="black" points="2262.62,-327.857 2273.15,-326.702 2264.2,-321.037 2262.62,-327.857"/>
179<text text-anchor="middle" x="2166" y="-327.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text>
134</g>180</g>
135<!-- split_ack_wait&#45;&gt;split_broadcast -->181<!-- split_ack_wait&#45;&gt;split_broadcast -->
136<g id="edge24" class="edge"><title>split_ack_wait&#45;&gt;split_broadcast</title>182<g id="edge24" class="edge"><title>split_ack_wait&#45;&gt;split_broadcast</title>
137<path fill="none" stroke="black" d="M1814.64,-90.9448C1807.69,-89.7544 1800.74,-88.7397 1794,-88 1720.66,-79.9496 1701.36,-80.1783 1628,-88 1624.71,-88.3505 1621.38,-88.7628 1618.01,-89.2264"/>183<path fill="none" stroke="black" d="M1899.33,-222.493C1888.37,-218.573 1877.04,-215.2 1866,-213 1808.89,-201.617 1743.56,-202.081 1691.71,-205.529"/>
138<polygon fill="black" stroke="black" points="1617.23,-85.8043 1607.87,-90.7602 1618.28,-92.7256 1617.23,-85.8043"/>184<polygon fill="black" stroke="black" points="1691.25,-202.053 1681.52,-206.256 1691.74,-209.035 1691.25,-202.053"/>
139<text text-anchor="middle" x="1711" y="-93.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text>185<text text-anchor="middle" x="1783" y="-218.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text>
186</g>
187<!-- conn_broken&#45;&gt;stop -->
188<g id="edge32" class="edge"><title>conn_broken&#45;&gt;stop</title>
189<path fill="none" stroke="black" d="M1434.61,-95.8477C1564.45,-92.5456 1841.16,-95.6985 2060,-168 2154.32,-199.163 2175.34,-218.326 2254,-279 2262.17,-285.305 2270.33,-292.782 2277.75,-300.185"/>
190<polygon fill="black" stroke="black" points="2275.42,-302.808 2284.91,-307.517 2280.43,-297.917 2275.42,-302.808"/>
191<text text-anchor="middle" x="1783" y="-127.4" font-family="Times Roman,serif" font-size="14.00">Write CONNBROKEN</text>
192</g>
193<!-- conn_warn&#45;&gt;loop -->
194<g id="edge34" class="edge"><title>conn_warn&#45;&gt;loop</title>
195<path fill="none" stroke="black" d="M1083.63,-435.301C1071.29,-427.246 1057.71,-419.822 1044,-415 972.758,-389.933 883.562,-389.406 832.05,-391.836"/>
196<polygon fill="black" stroke="black" points="831.758,-388.346 821.959,-392.373 832.131,-395.337 831.758,-388.346"/>
197<text text-anchor="middle" x="946" y="-420.4" font-family="Times Roman,serif" font-size="14.00">Write CONNWARN</text>
140</g>198</g>
141</g>199</g>
142</svg>200</svg>
143201
=== modified file 'server/acceptance/acceptanceclient.go'
--- server/acceptance/acceptanceclient.go 2014-04-09 19:30:53 +0000
+++ server/acceptance/acceptanceclient.go 2014-04-29 18:02:00 +0000
@@ -44,6 +44,7 @@
44 Levels map[string]int6444 Levels map[string]int64
45 Insecure bool // don't verify certs45 Insecure bool // don't verify certs
46 Prefix string // prefix for events46 Prefix string // prefix for events
47 Auth string
47 // connection48 // connection
48 Connection net.Conn49 Connection net.Conn
49}50}
@@ -73,6 +74,7 @@
73 Type string `json:"T"`74 Type string `json:"T"`
74 protocol.BroadcastMsg75 protocol.BroadcastMsg
75 protocol.NotificationsMsg76 protocol.NotificationsMsg
77 protocol.ConnWarnMsg
76}78}
7779
78// Run the session with the server, emits a stream of events.80// Run the session with the server, emits a stream of events.
@@ -93,6 +95,7 @@
93 "device": sess.Model,95 "device": sess.Model,
94 "channel": sess.ImageChannel,96 "channel": sess.ImageChannel,
95 },97 },
98 Authorization: sess.Auth,
96 })99 })
97 if err != nil {100 if err != nil {
98 return err101 return err
@@ -136,6 +139,8 @@
136 return err139 return err
137 }140 }
138 events <- fmt.Sprintf("%sbroadcast chan:%v app:%v topLevel:%d payloads:%s", sess.Prefix, recv.ChanId, recv.AppId, recv.TopLevel, pack)141 events <- fmt.Sprintf("%sbroadcast chan:%v app:%v topLevel:%d payloads:%s", sess.Prefix, recv.ChanId, recv.AppId, recv.TopLevel, pack)
142 case "warn":
143 events <- fmt.Sprintf("%swarn %s", sess.Prefix, recv.Reason)
139 }144 }
140 }145 }
141 return nil146 return nil
142147
=== modified file 'server/acceptance/cmd/acceptanceclient.go'
--- server/acceptance/cmd/acceptanceclient.go 2014-04-10 13:52:31 +0000
+++ server/acceptance/cmd/acceptanceclient.go 2014-04-29 18:02:00 +0000
@@ -48,13 +48,18 @@
48 fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <config.json> <device id>\n")48 fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <config.json> <device id>\n")
49 flag.PrintDefaults()49 flag.PrintDefaults()
50 }50 }
51 missingArg := func(what string) {
52 fmt.Fprintf(os.Stderr, "missing %s\n", what)
53 flag.Usage()
54 os.Exit(2)
55 }
51 flag.Parse()56 flag.Parse()
52 narg := flag.NArg()57 narg := flag.NArg()
53 switch {58 switch {
54 case narg < 1:59 case narg < 1:
55 log.Fatal("missing config file")60 missingArg("config file")
56 case narg < 2:61 case narg < 2:
57 log.Fatal("missing device-id")62 missingArg("device-id")
58 }63 }
59 configFName := flag.Arg(0)64 configFName := flag.Arg(0)
60 f, err := os.Open(configFName)65 f, err := os.Open(configFName)
6166
=== modified file 'server/acceptance/suites/broadcast.go'
--- server/acceptance/suites/broadcast.go 2014-04-07 19:39:19 +0000
+++ server/acceptance/suites/broadcast.go 2014-04-29 18:02:00 +0000
@@ -265,7 +265,11 @@
265265
266func (s *BroadcastAcceptanceSuite) TestGetHosts(c *C) {266func (s *BroadcastAcceptanceSuite) TestGetHosts(c *C) {
267 gh := gethosts.New("", s.ServerAPIURL+"/delivery-hosts", 2*time.Second)267 gh := gethosts.New("", s.ServerAPIURL+"/delivery-hosts", 2*time.Second)
268 hosts, err := gh.Get()268 host, err := gh.Get()
269 c.Assert(err, IsNil)269 c.Assert(err, IsNil)
270 c.Check(hosts, DeepEquals, []string{s.ServerAddr})270 expected := &gethosts.Host{
271 Domain: "localhost",
272 Hosts: []string{s.ServerAddr},
273 }
274 c.Check(host, DeepEquals, expected)
271}275}
272276
=== modified file 'server/acceptance/suites/suite.go'
--- server/acceptance/suites/suite.go 2014-04-03 16:47:47 +0000
+++ server/acceptance/suites/suite.go 2014-04-29 18:02:00 +0000
@@ -44,10 +44,19 @@
4444
45// Start a client.45// Start a client.
46func (h *ServerHandle) StartClient(c *C, devId string, levels map[string]int64) (events <-chan string, errorCh <-chan error, stop func()) {46func (h *ServerHandle) StartClient(c *C, devId string, levels map[string]int64) (events <-chan string, errorCh <-chan error, stop func()) {
47 return h.StartClientAuth(c, devId, levels, "")
48}
49
50// Start a client with auth.
51func (h *ServerHandle) StartClientAuth(c *C, devId string, levels map[string]int64, auth string) (events <-chan string, errorCh <-chan error, stop func()) {
47 errCh := make(chan error, 1)52 errCh := make(chan error, 1)
48 cliEvents := make(chan string, 10)53 cliEvents := make(chan string, 10)
49 sess := testClientSession(h.ServerAddr, devId, "m1", "img1", false)54 sess := testClientSession(h.ServerAddr, devId, "m1", "img1", false)
50 sess.Levels = levels55 sess.Levels = levels
56 sess.Auth = auth
57 if auth != "" {
58 sess.ExchangeTimeout = 5 * time.Second
59 }
51 err := sess.Dial()60 err := sess.Dial()
52 c.Assert(err, IsNil)61 c.Assert(err, IsNil)
53 clientShutdown := make(chan bool, 1) // abused as an atomic flag62 clientShutdown := make(chan bool, 1) // abused as an atomic flag
5463
=== modified file 'server/api/handlers_test.go'
--- server/api/handlers_test.go 2014-02-21 11:32:38 +0000
+++ server/api/handlers_test.go 2014-04-29 18:02:00 +0000
@@ -30,6 +30,7 @@
3030
31 . "launchpad.net/gocheck"31 . "launchpad.net/gocheck"
3232
33 "launchpad.net/ubuntu-push/protocol"
33 "launchpad.net/ubuntu-push/server/store"34 "launchpad.net/ubuntu-push/server/store"
34 helpers "launchpad.net/ubuntu-push/testing"35 helpers "launchpad.net/ubuntu-push/testing"
35)36)
@@ -142,11 +143,11 @@
142}143}
143144
144func (cbsend *checkBrokerSending) Broadcast(chanId store.InternalChannelId) {145func (cbsend *checkBrokerSending) Broadcast(chanId store.InternalChannelId) {
145 top, payloads, err := cbsend.store.GetChannelSnapshot(chanId)146 top, notifications, err := cbsend.store.GetChannelSnapshot(chanId)
146 cbsend.err = err147 cbsend.err = err
147 cbsend.chanId = chanId148 cbsend.chanId = chanId
148 cbsend.top = top149 cbsend.top = top
149 cbsend.payloads = payloads150 cbsend.payloads = protocol.ExtractPayloads(notifications)
150}151}
151152
152func (s *handlersSuite) TestDoBroadcast(c *C) {153func (s *handlersSuite) TestDoBroadcast(c *C) {
153154
=== modified file 'server/broker/broker.go'
--- server/broker/broker.go 2014-04-04 09:58:34 +0000
+++ server/broker/broker.go 2014-04-29 18:02:00 +0000
@@ -30,7 +30,7 @@
30// through them.30// through them.
31type Broker interface {31type Broker interface {
32 // Register the session.32 // Register the session.
33 Register(*protocol.ConnectMsg) (BrokerSession, error)33 Register(connMsg *protocol.ConnectMsg, sessionId string) (BrokerSession, error)
34 // Unregister the session.34 // Unregister the session.
35 Unregister(BrokerSession)35 Unregister(BrokerSession)
36}36}
3737
=== modified file 'server/broker/exchanges.go'
--- server/broker/exchanges.go 2014-04-04 13:19:10 +0000
+++ server/broker/exchanges.go 2014-04-29 18:02:00 +0000
@@ -28,18 +28,17 @@
2828
29// Scratch area for exchanges, sessions should hold one of these.29// Scratch area for exchanges, sessions should hold one of these.
30type ExchangesScratchArea struct {30type ExchangesScratchArea struct {
31 broadcastMsg protocol.BroadcastMsg31 broadcastMsg protocol.BroadcastMsg
32 ackMsg protocol.AckMsg32 ackMsg protocol.AckMsg
33 connBrokenMsg protocol.ConnBrokenMsg
34}33}
3534
36// BroadcastExchange leads a session through delivering a BROADCAST.35// BroadcastExchange leads a session through delivering a BROADCAST.
37// For simplicity it is fully public.36// For simplicity it is fully public.
38type BroadcastExchange struct {37type BroadcastExchange struct {
39 ChanId store.InternalChannelId38 ChanId store.InternalChannelId
40 TopLevel int6439 TopLevel int64
41 NotificationPayloads []json.RawMessage40 Notifications []protocol.Notification
42 Decoded []map[string]interface{}41 Decoded []map[string]interface{}
43}42}
4443
45// check interface already here44// check interface already here
@@ -47,18 +46,18 @@
4746
48// Init ensures the BroadcastExchange is fully initialized for the sessions.47// Init ensures the BroadcastExchange is fully initialized for the sessions.
49func (sbe *BroadcastExchange) Init() {48func (sbe *BroadcastExchange) Init() {
50 decoded := make([]map[string]interface{}, len(sbe.NotificationPayloads))49 decoded := make([]map[string]interface{}, len(sbe.Notifications))
51 sbe.Decoded = decoded50 sbe.Decoded = decoded
52 for i, p := range sbe.NotificationPayloads {51 for i, notif := range sbe.Notifications {
53 err := json.Unmarshal(p, &decoded[i])52 err := json.Unmarshal(notif.Payload, &decoded[i])
54 if err != nil {53 if err != nil {
55 decoded[i] = nil54 decoded[i] = nil
56 }55 }
57 }56 }
58}57}
5958
60func filterByLevel(clientLevel, topLevel int64, payloads []json.RawMessage) []json.RawMessage {59func filterByLevel(clientLevel, topLevel int64, notifs []protocol.Notification) []protocol.Notification {
61 c := int64(len(payloads))60 c := int64(len(notifs))
62 if c == 0 {61 if c == 0 {
63 return nil62 return nil
64 }63 }
@@ -67,32 +66,32 @@
67 delta = 166 delta = 1
68 }67 }
69 if delta < c {68 if delta < c {
70 return payloads[c-delta:]69 return notifs[c-delta:]
71 } else {70 } else {
72 return payloads71 return notifs
73 }72 }
74}73}
7574
76func channelFilter(tag string, chanId store.InternalChannelId, payloads []json.RawMessage, decoded []map[string]interface{}) []json.RawMessage {75func channelFilter(tag string, chanId store.InternalChannelId, notifs []protocol.Notification, decoded []map[string]interface{}) []json.RawMessage {
77 if len(payloads) != 0 && chanId == store.SystemInternalChannelId {76 if len(notifs) != 0 && chanId == store.SystemInternalChannelId {
78 decoded := decoded[len(decoded)-len(payloads):]77 decoded := decoded[len(decoded)-len(notifs):]
79 filtered := make([]json.RawMessage, 0)78 filtered := make([]json.RawMessage, 0)
80 for i, decoded1 := range decoded {79 for i, decoded1 := range decoded {
81 if _, ok := decoded1[tag]; ok {80 if _, ok := decoded1[tag]; ok {
82 filtered = append(filtered, payloads[i])81 filtered = append(filtered, notifs[i].Payload)
83 }82 }
84 }83 }
85 payloads = filtered84 return filtered
86 }85 }
87 return payloads86 return protocol.ExtractPayloads(notifs)
88}87}
8988
90// Prepare session for a BROADCAST.89// Prepare session for a BROADCAST.
91func (sbe *BroadcastExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) {90func (sbe *BroadcastExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) {
92 clientLevel := sess.Levels()[sbe.ChanId]91 clientLevel := sess.Levels()[sbe.ChanId]
93 payloads := filterByLevel(clientLevel, sbe.TopLevel, sbe.NotificationPayloads)92 notifs := filterByLevel(clientLevel, sbe.TopLevel, sbe.Notifications)
94 tag := fmt.Sprintf("%s/%s", sess.DeviceImageChannel(), sess.DeviceImageModel())93 tag := fmt.Sprintf("%s/%s", sess.DeviceImageChannel(), sess.DeviceImageModel())
95 payloads = channelFilter(tag, sbe.ChanId, payloads, sbe.Decoded)94 payloads := channelFilter(tag, sbe.ChanId, notifs, sbe.Decoded)
96 if len(payloads) == 0 && sbe.TopLevel >= clientLevel {95 if len(payloads) == 0 && sbe.TopLevel >= clientLevel {
97 // empty and don't need to force resync => do nothing96 // empty and don't need to force resync => do nothing
98 return nil, nil, ErrNop97 return nil, nil, ErrNop
@@ -119,23 +118,20 @@
119 return nil118 return nil
120}119}
121120
122// ConnBrokenExchange breaks a session giving a reason.121// ConnMetaExchange allows to send a CONNBROKEN or CONNWARN message.
123type ConnBrokenExchange struct {122type ConnMetaExchange struct {
124 Reason string123 Msg protocol.OnewayMsg
125}124}
126125
127// check interface already here126// check interface already here
128var _ Exchange = (*ConnBrokenExchange)(nil)127var _ Exchange = (*ConnMetaExchange)(nil)
129128
130// Prepare session for a CONNBROKEN.129// Prepare session for a CONNBROKEN/WARN.
131func (cbe *ConnBrokenExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) {130func (cbe *ConnMetaExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) {
132 scratchArea := sess.ExchangeScratchArea()131 return cbe.Msg, nil, nil
133 scratchArea.connBrokenMsg.Type = "connbroken"
134 scratchArea.connBrokenMsg.Reason = cbe.Reason
135 return &scratchArea.connBrokenMsg, nil, nil
136}132}
137133
138// CONNBROKEN isn't acked134// CONNBROKEN/WARN aren't acked.
139func (cbe *ConnBrokenExchange) Acked(sess BrokerSession, done bool) error {135func (cbe *ConnMetaExchange) Acked(sess BrokerSession, done bool) error {
140 panic("Acked should not get invoked on ConnBrokenExchange")136 panic("Acked should not get invoked on ConnMetaExchange")
141}137}
142138
=== modified file 'server/broker/exchanges_test.go'
--- server/broker/exchanges_test.go 2014-04-04 13:19:10 +0000
+++ server/broker/exchanges_test.go 2014-04-29 18:02:00 +0000
@@ -24,9 +24,11 @@
2424
25 . "launchpad.net/gocheck"25 . "launchpad.net/gocheck"
2626
27 "launchpad.net/ubuntu-push/protocol"
27 "launchpad.net/ubuntu-push/server/broker"28 "launchpad.net/ubuntu-push/server/broker"
28 "launchpad.net/ubuntu-push/server/broker/testing"29 "launchpad.net/ubuntu-push/server/broker/testing"
29 "launchpad.net/ubuntu-push/server/store"30 "launchpad.net/ubuntu-push/server/store"
31 help "launchpad.net/ubuntu-push/testing"
30)32)
3133
32func TestBroker(t *stdtesting.T) { TestingT(t) }34func TestBroker(t *stdtesting.T) { TestingT(t) }
@@ -39,11 +41,11 @@
39 exchg := &broker.BroadcastExchange{41 exchg := &broker.BroadcastExchange{
40 ChanId: store.SystemInternalChannelId,42 ChanId: store.SystemInternalChannelId,
41 TopLevel: 3,43 TopLevel: 3,
42 NotificationPayloads: []json.RawMessage{44 Notifications: help.Ns(
43 json.RawMessage(`{"a":"x"}`),45 json.RawMessage(`{"a":"x"}`),
44 json.RawMessage(`[]`),46 json.RawMessage(`[]`),
45 json.RawMessage(`{"a":"y"}`),47 json.RawMessage(`{"a":"y"}`),
46 },48 ),
47 }49 }
48 exchg.Init()50 exchg.Init()
49 c.Check(exchg.Decoded, DeepEquals, []map[string]interface{}{51 c.Check(exchg.Decoded, DeepEquals, []map[string]interface{}{
@@ -62,10 +64,10 @@
62 exchg := &broker.BroadcastExchange{64 exchg := &broker.BroadcastExchange{
63 ChanId: store.SystemInternalChannelId,65 ChanId: store.SystemInternalChannelId,
64 TopLevel: 3,66 TopLevel: 3,
65 NotificationPayloads: []json.RawMessage{67 Notifications: help.Ns(
66 json.RawMessage(`{"img1/m1":100}`),68 json.RawMessage(`{"img1/m1":100}`),
67 json.RawMessage(`{"img2/m2":200}`),69 json.RawMessage(`{"img2/m2":200}`),
68 },70 ),
69 }71 }
70 exchg.Init()72 exchg.Init()
71 outMsg, inMsg, err := exchg.Prepare(sess)73 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -88,9 +90,9 @@
88 ImageChannel: "img1",90 ImageChannel: "img1",
89 }91 }
90 exchg := &broker.BroadcastExchange{92 exchg := &broker.BroadcastExchange{
91 ChanId: store.SystemInternalChannelId,93 ChanId: store.SystemInternalChannelId,
92 TopLevel: 3,94 TopLevel: 3,
93 NotificationPayloads: []json.RawMessage{},95 Notifications: []protocol.Notification{},
94 }96 }
95 exchg.Init()97 exchg.Init()
96 outMsg, inMsg, err := exchg.Prepare(sess)98 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -108,9 +110,9 @@
108 ImageChannel: "img1",110 ImageChannel: "img1",
109 }111 }
110 exchg := &broker.BroadcastExchange{112 exchg := &broker.BroadcastExchange{
111 ChanId: store.SystemInternalChannelId,113 ChanId: store.SystemInternalChannelId,
112 TopLevel: 3,114 TopLevel: 3,
113 NotificationPayloads: []json.RawMessage{},115 Notifications: []protocol.Notification{},
114 }116 }
115 exchg.Init()117 exchg.Init()
116 outMsg, inMsg, err := exchg.Prepare(sess)118 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -133,9 +135,9 @@
133135
134 topLevel := int64(len(needsSplitting))136 topLevel := int64(len(needsSplitting))
135 exchg := &broker.BroadcastExchange{137 exchg := &broker.BroadcastExchange{
136 ChanId: store.SystemInternalChannelId,138 ChanId: store.SystemInternalChannelId,
137 TopLevel: topLevel,139 TopLevel: topLevel,
138 NotificationPayloads: needsSplitting,140 Notifications: help.Ns(needsSplitting...),
139 }141 }
140 exchg.Init()142 exchg.Init()
141 outMsg, _, err := exchg.Prepare(sess)143 outMsg, _, err := exchg.Prepare(sess)
@@ -152,10 +154,10 @@
152 exchg = &broker.BroadcastExchange{154 exchg = &broker.BroadcastExchange{
153 ChanId: store.SystemInternalChannelId,155 ChanId: store.SystemInternalChannelId,
154 TopLevel: topLevel + 2,156 TopLevel: topLevel + 2,
155 NotificationPayloads: []json.RawMessage{157 Notifications: help.Ns(
156 json.RawMessage(`{"img1/m1":"x"}`),158 json.RawMessage(`{"img1/m1":"x"}`),
157 json.RawMessage(`{"img1/m1":"y"}`),159 json.RawMessage(`{"img1/m1":"y"}`),
158 },160 ),
159 }161 }
160 exchg.Init()162 exchg.Init()
161 outMsg, _, err = exchg.Prepare(sess)163 outMsg, _, err = exchg.Prepare(sess)
@@ -173,9 +175,9 @@
173 exchg := &broker.BroadcastExchange{175 exchg := &broker.BroadcastExchange{
174 ChanId: store.SystemInternalChannelId,176 ChanId: store.SystemInternalChannelId,
175 TopLevel: 3,177 TopLevel: 3,
176 NotificationPayloads: []json.RawMessage{178 Notifications: help.Ns(
177 json.RawMessage(`{"img2/m1":1}`),179 json.RawMessage(`{"img2/m1":1}`),
178 },180 ),
179 }181 }
180 exchg.Init()182 exchg.Init()
181 outMsg, inMsg, err := exchg.Prepare(sess)183 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -202,10 +204,10 @@
202 exchg := &broker.BroadcastExchange{204 exchg := &broker.BroadcastExchange{
203 ChanId: store.SystemInternalChannelId,205 ChanId: store.SystemInternalChannelId,
204 TopLevel: 3,206 TopLevel: 3,
205 NotificationPayloads: []json.RawMessage{207 Notifications: help.Ns(
206 json.RawMessage(`{"img1/m1":100}`),208 json.RawMessage(`{"img1/m1":100}`),
207 json.RawMessage(`{"img1/m1":101}`),209 json.RawMessage(`{"img1/m1":101}`),
208 },210 ),
209 }211 }
210 exchg.Init()212 exchg.Init()
211 outMsg, inMsg, err := exchg.Prepare(sess)213 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -229,11 +231,11 @@
229 exchg := &broker.BroadcastExchange{231 exchg := &broker.BroadcastExchange{
230 ChanId: store.SystemInternalChannelId,232 ChanId: store.SystemInternalChannelId,
231 TopLevel: 5,233 TopLevel: 5,
232 NotificationPayloads: []json.RawMessage{234 Notifications: help.Ns(
233 json.RawMessage(`{"img1/m1":100}`),235 json.RawMessage(`{"img1/m1":100}`),
234 json.RawMessage(`{"img2/m2":200}`),236 json.RawMessage(`{"img2/m2":200}`),
235 json.RawMessage(`{"img1/m1":101}`),237 json.RawMessage(`{"img1/m1":101}`),
236 },238 ),
237 }239 }
238 exchg.Init()240 exchg.Init()
239 outMsg, inMsg, err := exchg.Prepare(sess)241 outMsg, inMsg, err := exchg.Prepare(sess)
@@ -249,16 +251,18 @@
249 c.Check(sess.LevelsMap[store.SystemInternalChannelId], Equals, int64(5))251 c.Check(sess.LevelsMap[store.SystemInternalChannelId], Equals, int64(5))
250}252}
251253
252func (s *exchangesSuite) TestConnBrokenExchange(c *C) {254func (s *exchangesSuite) TestConnMetaExchange(c *C) {
253 sess := &testing.TestBrokerSession{}255 sess := &testing.TestBrokerSession{}
254 cbe := &broker.ConnBrokenExchange{"REASON"}256 var msg protocol.OnewayMsg = &protocol.ConnWarnMsg{"connwarn", "REASON"}
257 cbe := &broker.ConnMetaExchange{msg}
255 outMsg, inMsg, err := cbe.Prepare(sess)258 outMsg, inMsg, err := cbe.Prepare(sess)
256 c.Assert(err, IsNil)259 c.Assert(err, IsNil)
260 c.Check(msg, Equals, outMsg)
257 c.Check(inMsg, IsNil) // no answer is expected261 c.Check(inMsg, IsNil) // no answer is expected
258 // check262 // check
259 marshalled, err := json.Marshal(outMsg)263 marshalled, err := json.Marshal(outMsg)
260 c.Assert(err, IsNil)264 c.Assert(err, IsNil)
261 c.Check(string(marshalled), Equals, `{"T":"connbroken","Reason":"REASON"}`)265 c.Check(string(marshalled), Equals, `{"T":"connwarn","Reason":"REASON"}`)
262266
263 c.Check(func() { cbe.Acked(nil, true) }, PanicMatches, "Acked should not get invoked on ConnBrokenExchange")267 c.Check(func() { cbe.Acked(nil, true) }, PanicMatches, "Acked should not get invoked on ConnMetaExchange")
264}268}
265269
=== modified file 'server/broker/exchg_impl_test.go'
--- server/broker/exchg_impl_test.go 2014-04-03 16:00:53 +0000
+++ server/broker/exchg_impl_test.go 2014-04-29 18:02:00 +0000
@@ -22,6 +22,7 @@
22 . "launchpad.net/gocheck"22 . "launchpad.net/gocheck"
2323
24 "launchpad.net/ubuntu-push/server/store"24 "launchpad.net/ubuntu-push/server/store"
25 help "launchpad.net/ubuntu-push/testing"
25)26)
2627
27type exchangesImplSuite struct{}28type exchangesImplSuite struct{}
@@ -29,27 +30,27 @@
29var _ = Suite(&exchangesImplSuite{})30var _ = Suite(&exchangesImplSuite{})
3031
31func (s *exchangesImplSuite) TestFilterByLevel(c *C) {32func (s *exchangesImplSuite) TestFilterByLevel(c *C) {
32 payloads := []json.RawMessage{33 notifs := help.Ns(
33 json.RawMessage(`{"a": 3}`),34 json.RawMessage(`{"a": 3}`),
34 json.RawMessage(`{"a": 4}`),35 json.RawMessage(`{"a": 4}`),
35 json.RawMessage(`{"a": 5}`),36 json.RawMessage(`{"a": 5}`),
36 }37 )
37 res := filterByLevel(5, 5, payloads)38 res := filterByLevel(5, 5, notifs)
38 c.Check(len(res), Equals, 0)39 c.Check(len(res), Equals, 0)
39 res = filterByLevel(4, 5, payloads)40 res = filterByLevel(4, 5, notifs)
40 c.Check(len(res), Equals, 1)41 c.Check(len(res), Equals, 1)
41 c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 5}`))42 c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 5}`))
42 res = filterByLevel(3, 5, payloads)43 res = filterByLevel(3, 5, notifs)
43 c.Check(len(res), Equals, 2)44 c.Check(len(res), Equals, 2)
44 c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 4}`))45 c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 4}`))
45 res = filterByLevel(2, 5, payloads)46 res = filterByLevel(2, 5, notifs)
46 c.Check(len(res), Equals, 3)47 c.Check(len(res), Equals, 3)
47 res = filterByLevel(1, 5, payloads)48 res = filterByLevel(1, 5, notifs)
48 c.Check(len(res), Equals, 3)49 c.Check(len(res), Equals, 3)
49 // too ahead, pick only last50 // too ahead, pick only last
50 res = filterByLevel(10, 5, payloads)51 res = filterByLevel(10, 5, notifs)
51 c.Check(len(res), Equals, 1)52 c.Check(len(res), Equals, 1)
52 c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 5}`))53 c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 5}`))
53}54}
5455
55func (s *exchangesImplSuite) TestFilterByLevelEmpty(c *C) {56func (s *exchangesImplSuite) TestFilterByLevelEmpty(c *C) {
@@ -71,18 +72,19 @@
71 err := json.Unmarshal(p, &decoded[i])72 err := json.Unmarshal(p, &decoded[i])
72 c.Assert(err, IsNil)73 c.Assert(err, IsNil)
73 }74 }
75 notifs := help.Ns(payloads...)
7476
75 other := store.InternalChannelId("1")77 other := store.InternalChannelId("1")
7678
77 c.Check(channelFilter("", store.SystemInternalChannelId, nil, nil), IsNil)79 c.Check(channelFilter("", store.SystemInternalChannelId, nil, nil), IsNil)
78 c.Check(channelFilter("", other, payloads[1:], decoded), DeepEquals, payloads[1:])80 c.Check(channelFilter("", other, notifs[1:], decoded), DeepEquals, payloads[1:])
7981
80 // use tag when channel is the sytem channel82 // use tag when channel is the sytem channel
8183
82 c.Check(channelFilter("c/z", store.SystemInternalChannelId, payloads, decoded), HasLen, 0)84 c.Check(channelFilter("c/z", store.SystemInternalChannelId, notifs, decoded), HasLen, 0)
8385
84 c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads, decoded), DeepEquals, []json.RawMessage{payloads[0], payloads[3]})86 c.Check(channelFilter("a/x", store.SystemInternalChannelId, notifs, decoded), DeepEquals, []json.RawMessage{payloads[0], payloads[3]})
8587
86 c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads[1:], decoded), DeepEquals, []json.RawMessage{payloads[3]})88 c.Check(channelFilter("a/x", store.SystemInternalChannelId, notifs[1:], decoded), DeepEquals, []json.RawMessage{payloads[3]})
8789
88}90}
8991
=== modified file 'server/broker/simple/simple.go'
--- server/broker/simple/simple.go 2014-04-11 08:47:18 +0000
+++ server/broker/simple/simple.go 2014-04-29 18:02:00 +0000
@@ -144,7 +144,7 @@
144 // find relevant channels, for now only system144 // find relevant channels, for now only system
145 channels := []store.InternalChannelId{store.SystemInternalChannelId}145 channels := []store.InternalChannelId{store.SystemInternalChannelId}
146 for _, chanId := range channels {146 for _, chanId := range channels {
147 topLevel, payloads, err := b.sto.GetChannelSnapshot(chanId)147 topLevel, notifications, err := b.sto.GetChannelSnapshot(chanId)
148 if err != nil {148 if err != nil {
149 // next broadcast will try again149 // next broadcast will try again
150 b.logger.Errorf("unsuccessful feed pending, get channel snapshot for %v: %v", chanId, err)150 b.logger.Errorf("unsuccessful feed pending, get channel snapshot for %v: %v", chanId, err)
@@ -153,9 +153,9 @@
153 clientLevel := sess.levels[chanId]153 clientLevel := sess.levels[chanId]
154 if clientLevel != topLevel {154 if clientLevel != topLevel {
155 broadcastExchg := &broker.BroadcastExchange{155 broadcastExchg := &broker.BroadcastExchange{
156 ChanId: chanId,156 ChanId: chanId,
157 TopLevel: topLevel,157 TopLevel: topLevel,
158 NotificationPayloads: payloads,158 Notifications: notifications,
159 }159 }
160 broadcastExchg.Init()160 broadcastExchg.Init()
161 sess.exchanges <- broadcastExchg161 sess.exchanges <- broadcastExchg
@@ -166,7 +166,7 @@
166166
167// Register registers a session with the broker. It feeds the session167// Register registers a session with the broker. It feeds the session
168// pending notifications as well.168// pending notifications as well.
169func (b *SimpleBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) {169func (b *SimpleBroker) Register(connect *protocol.ConnectMsg, sessionId string) (broker.BrokerSession, error) {
170 // xxx sanity check DeviceId170 // xxx sanity check DeviceId
171 model, err := broker.GetInfoString(connect, "device", "?")171 model, err := broker.GetInfoString(connect, "device", "?")
172 if err != nil {172 if err != nil {
@@ -224,7 +224,7 @@
224 } else { // register224 } else { // register
225 prev := b.registry[sess.deviceId]225 prev := b.registry[sess.deviceId]
226 if prev != nil { // kick it226 if prev != nil { // kick it
227 close(prev.exchanges)227 prev.exchanges <- nil
228 }228 }
229 b.registry[sess.deviceId] = sess229 b.registry[sess.deviceId] = sess
230 sess.registered = true230 sess.registered = true
@@ -233,16 +233,16 @@
233 case delivery := <-b.deliveryCh:233 case delivery := <-b.deliveryCh:
234 switch delivery.kind {234 switch delivery.kind {
235 case broadcastDelivery:235 case broadcastDelivery:
236 topLevel, payloads, err := b.sto.GetChannelSnapshot(delivery.chanId)236 topLevel, notifications, err := b.sto.GetChannelSnapshot(delivery.chanId)
237 if err != nil {237 if err != nil {
238 // next broadcast will try again238 // next broadcast will try again
239 b.logger.Errorf("unsuccessful broadcast, get channel snapshot for %v: %v", delivery.chanId, err)239 b.logger.Errorf("unsuccessful broadcast, get channel snapshot for %v: %v", delivery.chanId, err)
240 continue Loop240 continue Loop
241 }241 }
242 broadcastExchg := &broker.BroadcastExchange{242 broadcastExchg := &broker.BroadcastExchange{
243 ChanId: delivery.chanId,243 ChanId: delivery.chanId,
244 TopLevel: topLevel,244 TopLevel: topLevel,
245 NotificationPayloads: payloads,245 Notifications: notifications,
246 }246 }
247 broadcastExchg.Init()247 broadcastExchg.Init()
248 for _, sess := range b.registry {248 for _, sess := range b.registry {
249249
=== modified file 'server/broker/simple/simple_test.go'
--- server/broker/simple/simple_test.go 2014-04-03 16:00:53 +0000
+++ server/broker/simple/simple_test.go 2014-04-29 18:02:00 +0000
@@ -26,6 +26,7 @@
26 "launchpad.net/ubuntu-push/server/broker"26 "launchpad.net/ubuntu-push/server/broker"
27 "launchpad.net/ubuntu-push/server/broker/testing"27 "launchpad.net/ubuntu-push/server/broker/testing"
28 "launchpad.net/ubuntu-push/server/store"28 "launchpad.net/ubuntu-push/server/store"
29 help "launchpad.net/ubuntu-push/testing"
29)30)
3031
31func TestSimple(t *stdtesting.T) { TestingT(t) }32func TestSimple(t *stdtesting.T) { TestingT(t) }
@@ -58,10 +59,10 @@
58 c.Assert(len(sess.exchanges), Equals, 1)59 c.Assert(len(sess.exchanges), Equals, 1)
59 exchg1 := <-sess.exchanges60 exchg1 := <-sess.exchanges
60 c.Check(exchg1, DeepEquals, &broker.BroadcastExchange{61 c.Check(exchg1, DeepEquals, &broker.BroadcastExchange{
61 ChanId: store.SystemInternalChannelId,62 ChanId: store.SystemInternalChannelId,
62 TopLevel: 1,63 TopLevel: 1,
63 NotificationPayloads: []json.RawMessage{notification1},64 Notifications: help.Ns(notification1),
64 Decoded: []map[string]interface{}{decoded1},65 Decoded: []map[string]interface{}{decoded1},
65 })66 })
66}67}
6768
6869
=== modified file 'server/broker/testsuite/suite.go'
--- server/broker/testsuite/suite.go 2014-04-11 08:47:18 +0000
+++ server/broker/testsuite/suite.go 2014-04-29 18:02:00 +0000
@@ -30,7 +30,7 @@
30 "launchpad.net/ubuntu-push/server/broker"30 "launchpad.net/ubuntu-push/server/broker"
31 "launchpad.net/ubuntu-push/server/broker/testing"31 "launchpad.net/ubuntu-push/server/broker/testing"
32 "launchpad.net/ubuntu-push/server/store"32 "launchpad.net/ubuntu-push/server/store"
33 helpers "launchpad.net/ubuntu-push/testing"33 help "launchpad.net/ubuntu-push/testing"
34)34)
3535
36// The expected interface for tested brokers.36// The expected interface for tested brokers.
@@ -51,11 +51,11 @@
51 // Let us get to a broker.BroadcastExchange from an Exchange.51 // Let us get to a broker.BroadcastExchange from an Exchange.
52 RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange52 RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange
53 // private53 // private
54 testlog *helpers.TestLogger54 testlog *help.TestLogger
55}55}
5656
57func (s *CommonBrokerSuite) SetUpTest(c *C) {57func (s *CommonBrokerSuite) SetUpTest(c *C) {
58 s.testlog = helpers.NewTestLogger(c, "error")58 s.testlog = help.NewTestLogger(c, "error")
59}59}
6060
61var testBrokerConfig = &testing.TestBrokerConfig{10, 5}61var testBrokerConfig = &testing.TestBrokerConfig{10, 5}
@@ -89,7 +89,7 @@
89 "device": "model",89 "device": "model",
90 "channel": "daily",90 "channel": "daily",
91 },91 },
92 })92 }, "s1")
93 c.Assert(err, IsNil)93 c.Assert(err, IsNil)
94 c.Assert(s.RevealSession(b, "dev-1"), Equals, sess)94 c.Assert(s.RevealSession(b, "dev-1"), Equals, sess)
95 c.Assert(sess.DeviceIdentifier(), Equals, "dev-1")95 c.Assert(sess.DeviceIdentifier(), Equals, "dev-1")
@@ -101,7 +101,7 @@
101 }))101 }))
102 b.Unregister(sess)102 b.Unregister(sess)
103 // just to make sure the unregister was processed103 // just to make sure the unregister was processed
104 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""})104 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}, "s2")
105 c.Assert(err, IsNil)105 c.Assert(err, IsNil)
106 c.Check(s.RevealSession(b, "dev-1"), IsNil)106 c.Check(s.RevealSession(b, "dev-1"), IsNil)
107}107}
@@ -111,7 +111,7 @@
111 b := s.MakeBroker(sto, testBrokerConfig, nil)111 b := s.MakeBroker(sto, testBrokerConfig, nil)
112 b.Start()112 b.Start()
113 defer b.Stop()113 defer b.Stop()
114 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}})114 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}, "s1")
115 c.Check(err, FitsTypeOf, &broker.ErrAbort{})115 c.Check(err, FitsTypeOf, &broker.ErrAbort{})
116}116}
117117
@@ -123,11 +123,11 @@
123 info := map[string]interface{}{123 info := map[string]interface{}{
124 "device": -1,124 "device": -1,
125 }125 }
126 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", Info: info})126 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}, "s1")
127 c.Check(err, Equals, broker.ErrUnexpectedValue)127 c.Check(err, Equals, broker.ErrUnexpectedValue)
128 info["device"] = "m"128 info["device"] = "m"
129 info["channel"] = -1129 info["channel"] = -1
130 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", Info: info})130 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}, "s2")
131 c.Check(err, Equals, broker.ErrUnexpectedValue)131 c.Check(err, Equals, broker.ErrUnexpectedValue)
132}132}
133133
@@ -139,7 +139,7 @@
139 b := s.MakeBroker(sto, testBrokerConfig, nil)139 b := s.MakeBroker(sto, testBrokerConfig, nil)
140 b.Start()140 b.Start()
141 defer b.Stop()141 defer b.Stop()
142 sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})142 sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1")
143 c.Assert(err, IsNil)143 c.Assert(err, IsNil)
144 c.Check(len(sess.SessionChannel()), Equals, 1)144 c.Check(len(sess.SessionChannel()), Equals, 1)
145}145}
@@ -149,7 +149,7 @@
149 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)149 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
150 b.Start()150 b.Start()
151 defer b.Stop()151 defer b.Stop()
152 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})152 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1")
153 c.Assert(err, IsNil)153 c.Assert(err, IsNil)
154 // but154 // but
155 c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")155 c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")
@@ -160,22 +160,25 @@
160 b := s.MakeBroker(sto, testBrokerConfig, nil)160 b := s.MakeBroker(sto, testBrokerConfig, nil)
161 b.Start()161 b.Start()
162 defer b.Stop()162 defer b.Stop()
163 sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})163 sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1")
164 c.Assert(err, IsNil)164 c.Assert(err, IsNil)
165 sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})165 sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s2")
166 c.Assert(err, IsNil)166 c.Assert(err, IsNil)
167 checkAndFalse := false167 // previous session got signaled by sending nil on its channel
168 // previous session got signaled by closing its channel168 var sentinel broker.Exchange
169 got := false
169 select {170 select {
170 case _, ok := <-sess1.SessionChannel():171 case sentinel = <-sess1.SessionChannel():
171 checkAndFalse = ok == false172 got = true
172 default:173 case <-time.After(5 * time.Second):
174 c.Fatal("taking too long to get sentinel")
173 }175 }
174 c.Check(checkAndFalse, Equals, true)176 c.Check(got, Equals, true)
177 c.Check(sentinel, IsNil)
175 c.Assert(s.RevealSession(b, "dev-1"), Equals, sess2)178 c.Assert(s.RevealSession(b, "dev-1"), Equals, sess2)
176 b.Unregister(sess1)179 b.Unregister(sess1)
177 // just to make sure the unregister was processed180 // just to make sure the unregister was processed
178 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""})181 _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}, "s3")
179 c.Assert(err, IsNil)182 c.Assert(err, IsNil)
180 c.Check(s.RevealSession(b, "dev-1"), Equals, sess2)183 c.Check(s.RevealSession(b, "dev-1"), Equals, sess2)
181}184}
@@ -187,9 +190,9 @@
187 b := s.MakeBroker(sto, testBrokerConfig, nil)190 b := s.MakeBroker(sto, testBrokerConfig, nil)
188 b.Start()191 b.Start()
189 defer b.Stop()192 defer b.Stop()
190 sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})193 sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1")
191 c.Assert(err, IsNil)194 c.Assert(err, IsNil)
192 sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-2"})195 sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-2"}, "s2")
193 c.Assert(err, IsNil)196 c.Assert(err, IsNil)
194 // add notification to channel *after* the registrations197 // add notification to channel *after* the registrations
195 muchLater := time.Now().Add(10 * time.Minute)198 muchLater := time.Now().Add(10 * time.Minute)
@@ -200,10 +203,10 @@
200 c.Fatal("taking too long to get broadcast exchange")203 c.Fatal("taking too long to get broadcast exchange")
201 case exchg1 := <-sess1.SessionChannel():204 case exchg1 := <-sess1.SessionChannel():
202 c.Check(s.RevealBroadcastExchange(exchg1), DeepEquals, &broker.BroadcastExchange{205 c.Check(s.RevealBroadcastExchange(exchg1), DeepEquals, &broker.BroadcastExchange{
203 ChanId: store.SystemInternalChannelId,206 ChanId: store.SystemInternalChannelId,
204 TopLevel: 1,207 TopLevel: 1,
205 NotificationPayloads: []json.RawMessage{notification1},208 Notifications: help.Ns(notification1),
206 Decoded: []map[string]interface{}{decoded1},209 Decoded: []map[string]interface{}{decoded1},
207 })210 })
208 }211 }
209 select {212 select {
@@ -211,10 +214,10 @@
211 c.Fatal("taking too long to get broadcast exchange")214 c.Fatal("taking too long to get broadcast exchange")
212 case exchg2 := <-sess2.SessionChannel():215 case exchg2 := <-sess2.SessionChannel():
213 c.Check(s.RevealBroadcastExchange(exchg2), DeepEquals, &broker.BroadcastExchange{216 c.Check(s.RevealBroadcastExchange(exchg2), DeepEquals, &broker.BroadcastExchange{
214 ChanId: store.SystemInternalChannelId,217 ChanId: store.SystemInternalChannelId,
215 TopLevel: 1,218 TopLevel: 1,
216 NotificationPayloads: []json.RawMessage{notification1},219 Notifications: help.Ns(notification1),
217 Decoded: []map[string]interface{}{decoded1},220 Decoded: []map[string]interface{}{decoded1},
218 })221 })
219 }222 }
220}223}
@@ -224,7 +227,7 @@
224 countdownToFail int227 countdownToFail int
225}228}
226229
227func (sto *testFailingStore) GetChannelSnapshot(chanId store.InternalChannelId) (int64, []json.RawMessage, error) {230func (sto *testFailingStore) GetChannelSnapshot(chanId store.InternalChannelId) (int64, []protocol.Notification, error) {
228 if sto.countdownToFail == 0 {231 if sto.countdownToFail == 0 {
229 return 0, nil, errors.New("get channel snapshot fail")232 return 0, nil, errors.New("get channel snapshot fail")
230 }233 }
@@ -241,7 +244,7 @@
241 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)244 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
242 b.Start()245 b.Start()
243 defer b.Stop()246 defer b.Stop()
244 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})247 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1")
245 c.Assert(err, IsNil)248 c.Assert(err, IsNil)
246 b.Broadcast(store.SystemInternalChannelId)249 b.Broadcast(store.SystemInternalChannelId)
247 select {250 select {
248251
=== modified file 'server/session/session.go'
--- server/session/session.go 2014-04-11 08:47:18 +0000
+++ server/session/session.go 2014-04-29 18:02:00 +0000
@@ -18,6 +18,7 @@
18package session18package session
1919
20import (20import (
21 "errors"
21 "net"22 "net"
22 "time"23 "time"
2324
@@ -35,7 +36,7 @@
35}36}
3637
37// sessionStart manages the start of the protocol session.38// sessionStart manages the start of the protocol session.
38func sessionStart(proto protocol.Protocol, brkr broker.Broker, cfg SessionConfig) (broker.BrokerSession, error) {39func sessionStart(proto protocol.Protocol, brkr broker.Broker, cfg SessionConfig, sessionId string) (broker.BrokerSession, error) {
39 var connMsg protocol.ConnectMsg40 var connMsg protocol.ConnectMsg
40 proto.SetDeadline(time.Now().Add(cfg.ExchangeTimeout()))41 proto.SetDeadline(time.Now().Add(cfg.ExchangeTimeout()))
41 err := proto.ReadMessage(&connMsg)42 err := proto.ReadMessage(&connMsg)
@@ -52,9 +53,11 @@
52 if err != nil {53 if err != nil {
53 return nil, err54 return nil, err
54 }55 }
55 return brkr.Register(&connMsg)56 return brkr.Register(&connMsg, sessionId)
56}57}
5758
59var errOneway = errors.New("oneway")
60
58// exchange writes outMsg message, reads answer in inMsg61// exchange writes outMsg message, reads answer in inMsg
59func exchange(proto protocol.Protocol, outMsg, inMsg interface{}, exchangeTimeout time.Duration) error {62func exchange(proto protocol.Protocol, outMsg, inMsg interface{}, exchangeTimeout time.Duration) error {
60 proto.SetDeadline(time.Now().Add(exchangeTimeout))63 proto.SetDeadline(time.Now().Add(exchangeTimeout))
@@ -62,7 +65,10 @@
62 if err != nil {65 if err != nil {
63 return err66 return err
64 }67 }
65 if inMsg == nil { // no answer expected, breaking connection68 if inMsg == nil { // no answer expected
69 if outMsg.(protocol.OnewayMsg).OnewayContinue() {
70 return errOneway
71 }
66 return &broker.ErrAbort{"session broken for reason"}72 return &broker.ErrAbort{"session broken for reason"}
67 }73 }
68 err = proto.ReadMessage(inMsg)74 err = proto.ReadMessage(inMsg)
@@ -78,6 +84,10 @@
78 exchangeTimeout := cfg.ExchangeTimeout()84 exchangeTimeout := cfg.ExchangeTimeout()
79 pingTimer := time.NewTimer(pingInterval)85 pingTimer := time.NewTimer(pingInterval)
80 intervalStart := time.Now()86 intervalStart := time.Now()
87 pingTimerReset := func() {
88 pingTimer.Reset(pingInterval)
89 intervalStart = time.Now()
90 }
81 ch := sess.SessionChannel()91 ch := sess.SessionChannel()
82Loop:92Loop:
83 for {93 for {
@@ -93,16 +103,15 @@
93 if pongMsg.Type != "pong" {103 if pongMsg.Type != "pong" {
94 return &broker.ErrAbort{"expected PONG message"}104 return &broker.ErrAbort{"expected PONG message"}
95 }105 }
96 pingTimer.Reset(pingInterval)106 pingTimerReset()
97 case exchg, ok := <-ch:107 case exchg := <-ch:
98 pingTimer.Stop()108 pingTimer.Stop()
99 if !ok {109 if exchg == nil {
100 return &broker.ErrAbort{"terminated"}110 return &broker.ErrAbort{"terminated"}
101 }111 }
102 outMsg, inMsg, err := exchg.Prepare(sess)112 outMsg, inMsg, err := exchg.Prepare(sess)
103 if err == broker.ErrNop { // nothing to do113 if err == broker.ErrNop { // nothing to do
104 pingTimer.Reset(pingInterval)114 pingTimerReset()
105 intervalStart = time.Now()
106 continue Loop115 continue Loop
107 }116 }
108 if err != nil {117 if err != nil {
@@ -111,12 +120,15 @@
111 for {120 for {
112 done := outMsg.Split()121 done := outMsg.Split()
113 err = exchange(proto, outMsg, inMsg, exchangeTimeout)122 err = exchange(proto, outMsg, inMsg, exchangeTimeout)
123 if err == errOneway {
124 pingTimerReset()
125 continue Loop
126 }
114 if err != nil {127 if err != nil {
115 return err128 return err
116 }129 }
117 if done {130 if done {
118 pingTimer.Reset(pingInterval)131 pingTimerReset()
119 intervalStart = time.Now()
120 }132 }
121 err = exchg.Acked(sess, done)133 err = exchg.Acked(sess, done)
122 if err != nil {134 if err != nil {
@@ -142,7 +154,7 @@
142 return track.End(&broker.ErrAbort{"unexpected wire format version"})154 return track.End(&broker.ErrAbort{"unexpected wire format version"})
143 }155 }
144 proto := protocol.NewProtocol0(conn)156 proto := protocol.NewProtocol0(conn)
145 sess, err := sessionStart(proto, brkr, cfg)157 sess, err := sessionStart(proto, brkr, cfg, track.SessionId())
146 if err != nil {158 if err != nil {
147 return track.End(err)159 return track.End(err)
148 }160 }
149161
=== modified file 'server/session/session_test.go'
--- server/session/session_test.go 2014-04-11 08:47:18 +0000
+++ server/session/session_test.go 2014-04-29 18:02:00 +0000
@@ -130,8 +130,8 @@
130 return &testBroker{registration: make(chan interface{}, 2)}130 return &testBroker{registration: make(chan interface{}, 2)}
131}131}
132132
133func (tb *testBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) {133func (tb *testBroker) Register(connect *protocol.ConnectMsg, sessionId string) (broker.BrokerSession, error) {
134 tb.registration <- "register " + connect.DeviceId134 tb.registration <- fmt.Sprintf("register %s %s", connect.DeviceId, sessionId)
135 return &testing.TestBrokerSession{DeviceId: connect.DeviceId}, tb.err135 return &testing.TestBrokerSession{DeviceId: connect.DeviceId}, tb.err
136}136}
137137
@@ -148,7 +148,7 @@
148 brkr := newTestBroker()148 brkr := newTestBroker()
149 go func() {149 go func() {
150 var err error150 var err error
151 sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout)151 sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout, "s1")
152 errCh <- err152 errCh <- err
153 }()153 }()
154 c.Check(takeNext(down), Equals, "deadline 5ms")154 c.Check(takeNext(down), Equals, "deadline 5ms")
@@ -160,7 +160,7 @@
160 up <- nil // no write error160 up <- nil // no write error
161 err := <-errCh161 err := <-errCh
162 c.Check(err, IsNil)162 c.Check(err, IsNil)
163 c.Check(takeNext(brkr.registration), Equals, "register dev-1")163 c.Check(takeNext(brkr.registration), Equals, "register dev-1 s1")
164 c.Check(sess.DeviceIdentifier(), Equals, "dev-1")164 c.Check(sess.DeviceIdentifier(), Equals, "dev-1")
165}165}
166166
@@ -175,7 +175,7 @@
175 brkr.err = errRegister175 brkr.err = errRegister
176 go func() {176 go func() {
177 var err error177 var err error
178 sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout)178 sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout, "s2")
179 errCh <- err179 errCh <- err
180 }()180 }()
181 up <- protocol.ConnectMsg{Type: "connect", ClientVer: "1", DeviceId: "dev-1"}181 up <- protocol.ConnectMsg{Type: "connect", ClientVer: "1", DeviceId: "dev-1"}
@@ -190,7 +190,7 @@
190 down := make(chan interface{}, 5)190 down := make(chan interface{}, 5)
191 tp := &testProtocol{up, down}191 tp := &testProtocol{up, down}
192 up <- io.ErrUnexpectedEOF192 up <- io.ErrUnexpectedEOF
193 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout)193 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s3")
194 c.Check(err, Equals, io.ErrUnexpectedEOF)194 c.Check(err, Equals, io.ErrUnexpectedEOF)
195}195}
196196
@@ -200,7 +200,7 @@
200 tp := &testProtocol{up, down}200 tp := &testProtocol{up, down}
201 up <- protocol.ConnectMsg{Type: "connect"}201 up <- protocol.ConnectMsg{Type: "connect"}
202 up <- io.ErrUnexpectedEOF202 up <- io.ErrUnexpectedEOF
203 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout)203 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s4")
204 c.Check(err, Equals, io.ErrUnexpectedEOF)204 c.Check(err, Equals, io.ErrUnexpectedEOF)
205 // sanity205 // sanity
206 c.Check(takeNext(down), Matches, "deadline.*")206 c.Check(takeNext(down), Matches, "deadline.*")
@@ -212,7 +212,7 @@
212 down := make(chan interface{}, 5)212 down := make(chan interface{}, 5)
213 tp := &testProtocol{up, down}213 tp := &testProtocol{up, down}
214 up <- protocol.ConnectMsg{Type: "what"}214 up <- protocol.ConnectMsg{Type: "what"}
215 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout)215 _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s5")
216 c.Check(err, DeepEquals, &broker.ErrAbort{"expected CONNECT message"})216 c.Check(err, DeepEquals, &broker.ErrAbort{"expected CONNECT message"})
217}217}
218218
@@ -222,14 +222,14 @@
222}222}
223223
224func (s *sessionSuite) TestSessionLoop(c *C) {224func (s *sessionSuite) TestSessionLoop(c *C) {
225 nopTrack := NewTracker(s.testlog)225 track := &testTracker{NewTracker(s.testlog), make(chan interface{}, 2)}
226 errCh := make(chan error, 1)226 errCh := make(chan error, 1)
227 up := make(chan interface{}, 5)227 up := make(chan interface{}, 5)
228 down := make(chan interface{}, 5)228 down := make(chan interface{}, 5)
229 tp := &testProtocol{up, down}229 tp := &testProtocol{up, down}
230 sess := &testing.TestBrokerSession{}230 sess := &testing.TestBrokerSession{}
231 go func() {231 go func() {
232 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)232 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, track)
233 }()233 }()
234 c.Check(takeNext(down), Equals, "deadline 2ms")234 c.Check(takeNext(down), Equals, "deadline 2ms")
235 c.Check(takeNext(down), DeepEquals, protocol.PingPongMsg{Type: "ping"})235 c.Check(takeNext(down), DeepEquals, protocol.PingPongMsg{Type: "ping"})
@@ -241,6 +241,9 @@
241 up <- io.ErrUnexpectedEOF241 up <- io.ErrUnexpectedEOF
242 err := <-errCh242 err := <-errCh
243 c.Check(err, Equals, io.ErrUnexpectedEOF)243 c.Check(err, Equals, io.ErrUnexpectedEOF)
244 c.Check(track.interval, HasLen, 2)
245 c.Check((<-track.interval).(time.Duration) <= 8*time.Millisecond, Equals, true)
246 c.Check((<-track.interval).(time.Duration) <= 8*time.Millisecond, Equals, true)
244}247}
245248
246func (s *sessionSuite) TestSessionLoopWriteError(c *C) {249func (s *sessionSuite) TestSessionLoopWriteError(c *C) {
@@ -357,7 +360,7 @@
357 go func() {360 go func() {
358 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)361 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)
359 }()362 }()
360 close(exchanges)363 exchanges <- nil
361 err := <-errCh364 err := <-errCh
362 c.Check(err, DeepEquals, &broker.ErrAbort{"terminated"})365 c.Check(err, DeepEquals, &broker.ErrAbort{"terminated"})
363}366}
@@ -477,18 +480,44 @@
477 down := make(chan interface{}, 5)480 down := make(chan interface{}, 5)
478 tp := &testProtocol{up, down}481 tp := &testProtocol{up, down}
479 exchanges := make(chan broker.Exchange, 1)482 exchanges := make(chan broker.Exchange, 1)
480 exchanges <- &broker.ConnBrokenExchange{"REASON"}483 msg := &protocol.ConnBrokenMsg{"connbroken", "BREASON"}
484 exchanges <- &broker.ConnMetaExchange{msg}
481 sess := &testing.TestBrokerSession{Exchanges: exchanges}485 sess := &testing.TestBrokerSession{Exchanges: exchanges}
482 go func() {486 go func() {
483 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)487 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)
484 }()488 }()
485 c.Check(takeNext(down), Equals, "deadline 2ms")489 c.Check(takeNext(down), Equals, "deadline 2ms")
486 c.Check(takeNext(down), DeepEquals, protocol.ConnBrokenMsg{"connbroken", "REASON"})490 c.Check(takeNext(down), DeepEquals, protocol.ConnBrokenMsg{"connbroken", "BREASON"})
487 up <- nil // no write error491 up <- nil // no write error
488 err := <-errCh492 err := <-errCh
489 c.Check(err, DeepEquals, &broker.ErrAbort{"session broken for reason"})493 c.Check(err, DeepEquals, &broker.ErrAbort{"session broken for reason"})
490}494}
491495
496func (s *sessionSuite) TestSessionLoopConnWarnExchange(c *C) {
497 nopTrack := NewTracker(s.testlog)
498 errCh := make(chan error, 1)
499 up := make(chan interface{}, 5)
500 down := make(chan interface{}, 5)
501 tp := &testProtocol{up, down}
502 exchanges := make(chan broker.Exchange, 1)
503 msg := &protocol.ConnWarnMsg{"connwarn", "WREASON"}
504 exchanges <- &broker.ConnMetaExchange{msg}
505 sess := &testing.TestBrokerSession{Exchanges: exchanges}
506 go func() {
507 errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack)
508 }()
509 c.Check(takeNext(down), Equals, "deadline 2ms")
510 c.Check(takeNext(down), DeepEquals, protocol.ConnWarnMsg{"connwarn", "WREASON"})
511 up <- nil // no write error
512 // session continues
513 c.Check(takeNext(down), Equals, "deadline 2ms")
514 c.Check(takeNext(down), DeepEquals, protocol.PingPongMsg{Type: "ping"})
515 up <- nil // no write error
516 up <- io.EOF
517 err := <-errCh
518 c.Check(err, Equals, io.EOF)
519}
520
492type testTracker struct {521type testTracker struct {
493 SessionTracker522 SessionTracker
494 interval chan interface{}523 interval chan interface{}
@@ -593,7 +622,7 @@
593 msg, err = downStream.ReadBytes(byte('}'))622 msg, err = downStream.ReadBytes(byte('}'))
594 c.Check(err, IsNil)623 c.Check(err, IsNil)
595 c.Check(msg, DeepEquals, []byte("\x00\x0c{\"T\":\"ping\"}"))624 c.Check(msg, DeepEquals, []byte("\x00\x0c{\"T\":\"ping\"}"))
596 c.Check(takeNext(brkr.registration), Equals, "register DEV")625 c.Check(takeNext(brkr.registration), Equals, "register DEV "+track.SessionId())
597 c.Check(len(brkr.registration), Equals, 0) // not yet unregistered626 c.Check(len(brkr.registration), Equals, 0) // not yet unregistered
598 cli.Close()627 cli.Close()
599 err = <-errCh628 err = <-errCh
600629
=== modified file 'server/session/tracker.go'
--- server/session/tracker.go 2014-02-10 23:19:08 +0000
+++ server/session/tracker.go 2014-04-29 18:02:00 +0000
@@ -17,6 +17,7 @@
17package session17package session
1818
19import (19import (
20 "fmt"
20 "net"21 "net"
21 "time"22 "time"
2223
@@ -29,6 +30,8 @@
29 logger.Logger30 logger.Logger
30 // Session got started.31 // Session got started.
31 Start(WithRemoteAddr)32 Start(WithRemoteAddr)
33 // SessionId
34 SessionId() string
32 // Session got registered with broker as sess BrokerSession.35 // Session got registered with broker as sess BrokerSession.
33 Registered(sess broker.BrokerSession)36 Registered(sess broker.BrokerSession)
34 // Report effective elapsed ping interval.37 // Report effective elapsed ping interval.
@@ -47,7 +50,7 @@
47// Tracker implements SessionTracker simply.50// Tracker implements SessionTracker simply.
48type tracker struct {51type tracker struct {
49 logger.Logger52 logger.Logger
50 sessionId int64 // xxx use timeuuid later53 sessionId string
51}54}
5255
53func NewTracker(logger logger.Logger) SessionTracker {56func NewTracker(logger logger.Logger) SessionTracker {
@@ -55,18 +58,22 @@
55}58}
5659
57func (trk *tracker) Start(conn WithRemoteAddr) {60func (trk *tracker) Start(conn WithRemoteAddr) {
58 trk.sessionId = time.Now().UnixNano() - sessionsEpoch61 trk.sessionId = fmt.Sprintf("%x", time.Now().UnixNano()-sessionsEpoch)
59 trk.Debugf("session(%x) connected %v", trk.sessionId, conn.RemoteAddr())62 trk.Debugf("session(%s) connected %v", trk.sessionId, conn.RemoteAddr())
63}
64
65func (trk *tracker) SessionId() string {
66 return trk.sessionId
60}67}
6168
62func (trk *tracker) Registered(sess broker.BrokerSession) {69func (trk *tracker) Registered(sess broker.BrokerSession) {
63 trk.Infof("session(%x) registered %v", trk.sessionId, sess.DeviceIdentifier())70 trk.Infof("session(%s) registered %v", trk.sessionId, sess.DeviceIdentifier())
64}71}
6572
66func (trk *tracker) EffectivePingInterval(time.Duration) {73func (trk *tracker) EffectivePingInterval(time.Duration) {
67}74}
6875
69func (trk *tracker) End(err error) error {76func (trk *tracker) End(err error) error {
70 trk.Debugf("session(%x) ended with: %v", trk.sessionId, err)77 trk.Debugf("session(%s) ended with: %v", trk.sessionId, err)
71 return err78 return err
72}79}
7380
=== modified file 'server/session/tracker_test.go'
--- server/session/tracker_test.go 2014-02-10 23:19:08 +0000
+++ server/session/tracker_test.go 2014-04-29 18:02:00 +0000
@@ -46,8 +46,8 @@
46func (s *trackerSuite) TestSessionTrackStart(c *C) {46func (s *trackerSuite) TestSessionTrackStart(c *C) {
47 track := NewTracker(s.testlog)47 track := NewTracker(s.testlog)
48 track.Start(&testRemoteAddrable{})48 track.Start(&testRemoteAddrable{})
49 c.Check(track.(*tracker).sessionId, Not(Equals), 0)49 c.Check(track.SessionId(), Not(Equals), "")
50 regExpected := fmt.Sprintf(`DEBUG session\(%x\) connected 127\.0\.0\.1:9999\n`, track.(*tracker).sessionId)50 regExpected := fmt.Sprintf(`DEBUG session\(%s\) connected 127\.0\.0\.1:9999\n`, track.SessionId())
51 c.Check(s.testlog.Captured(), Matches, regExpected)51 c.Check(s.testlog.Captured(), Matches, regExpected)
52}52}
5353
@@ -55,7 +55,7 @@
55 track := NewTracker(s.testlog)55 track := NewTracker(s.testlog)
56 track.Start(&testRemoteAddrable{})56 track.Start(&testRemoteAddrable{})
57 track.Registered(&testing.TestBrokerSession{DeviceId: "DEV-ID"})57 track.Registered(&testing.TestBrokerSession{DeviceId: "DEV-ID"})
58 regExpected := fmt.Sprintf(`.*connected.*\nINFO session\(%x\) registered DEV-ID\n`, track.(*tracker).sessionId)58 regExpected := fmt.Sprintf(`.*connected.*\nINFO session\(%s\) registered DEV-ID\n`, track.SessionId())
59 c.Check(s.testlog.Captured(), Matches, regExpected)59 c.Check(s.testlog.Captured(), Matches, regExpected)
60}60}
6161
@@ -63,6 +63,6 @@
63 track := NewTracker(s.testlog)63 track := NewTracker(s.testlog)
64 track.Start(&testRemoteAddrable{})64 track.Start(&testRemoteAddrable{})
65 track.End(&broker.ErrAbort{})65 track.End(&broker.ErrAbort{})
66 regExpected := fmt.Sprintf(`.*connected.*\nDEBUG session\(%x\) ended with: session aborted \(\)\n`, track.(*tracker).sessionId)66 regExpected := fmt.Sprintf(`.*connected.*\nDEBUG session\(%s\) ended with: session aborted \(\)\n`, track.SessionId())
67 c.Check(s.testlog.Captured(), Matches, regExpected)67 c.Check(s.testlog.Captured(), Matches, regExpected)
68}68}
6969
=== modified file 'server/store/inmemory.go'
--- server/store/inmemory.go 2014-02-18 14:19:05 +0000
+++ server/store/inmemory.go 2014-04-29 18:02:00 +0000
@@ -20,11 +20,13 @@
20 "encoding/json"20 "encoding/json"
21 "sync"21 "sync"
22 "time"22 "time"
23
24 "launchpad.net/ubuntu-push/protocol"
23)25)
2426
25// one stored notification27// one stored notification
26type notification struct {28type notification struct {
27 payload json.RawMessage29 protocol.Notification
28 expiration time.Time30 expiration time.Time
29}31}
3032
@@ -63,14 +65,14 @@
63 }65 }
64 prev.topLevel++66 prev.topLevel++
65 prev.notifications = append(prev.notifications, notification{67 prev.notifications = append(prev.notifications, notification{
66 payload: notificationPayload,68 Notification: protocol.Notification{Payload: notificationPayload},
67 expiration: expiration,69 expiration: expiration,
68 })70 })
69 sto.store[chanId] = prev71 sto.store[chanId] = prev
70 return nil72 return nil
71}73}
7274
73func (sto *InMemoryPendingStore) GetChannelSnapshot(chanId InternalChannelId) (int64, []json.RawMessage, error) {75func (sto *InMemoryPendingStore) GetChannelSnapshot(chanId InternalChannelId) (int64, []protocol.Notification, error) {
74 sto.lock.Lock()76 sto.lock.Lock()
75 defer sto.lock.Unlock()77 defer sto.lock.Unlock()
76 channel, ok := sto.store[chanId]78 channel, ok := sto.store[chanId]
@@ -79,13 +81,13 @@
79 }81 }
80 topLevel := channel.topLevel82 topLevel := channel.topLevel
81 n := len(channel.notifications)83 n := len(channel.notifications)
82 res := make([]json.RawMessage, 0, n)84 res := make([]protocol.Notification, 0, n)
83 now := time.Now()85 now := time.Now()
84 for _, notification := range channel.notifications {86 for _, notification := range channel.notifications {
85 if notification.expiration.Before(now) {87 if notification.expiration.Before(now) {
86 continue88 continue
87 }89 }
88 res = append(res, notification.payload)90 res = append(res, notification.Notification)
89 }91 }
90 return topLevel, res, nil92 return topLevel, res, nil
91}93}
9294
=== modified file 'server/store/inmemory_test.go'
--- server/store/inmemory_test.go 2014-02-14 12:38:38 +0000
+++ server/store/inmemory_test.go 2014-04-29 18:02:00 +0000
@@ -21,6 +21,9 @@
21 "time"21 "time"
2222
23 . "launchpad.net/gocheck"23 . "launchpad.net/gocheck"
24
25 "launchpad.net/ubuntu-push/protocol"
26 help "launchpad.net/ubuntu-push/testing"
24)27)
2528
26type inMemorySuite struct{}29type inMemorySuite struct{}
@@ -45,7 +48,7 @@
45 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)48 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)
46 c.Assert(err, IsNil)49 c.Assert(err, IsNil)
47 c.Check(top, Equals, int64(0))50 c.Check(top, Equals, int64(0))
48 c.Check(res, DeepEquals, []json.RawMessage(nil))51 c.Check(res, DeepEquals, []protocol.Notification(nil))
49}52}
5053
51func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshot(c *C) {54func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshot(c *C) {
@@ -61,7 +64,7 @@
61 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)64 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)
62 c.Assert(err, IsNil)65 c.Assert(err, IsNil)
63 c.Check(top, Equals, int64(2))66 c.Check(top, Equals, int64(2))
64 c.Check(res, DeepEquals, []json.RawMessage{notification1, notification2})67 c.Check(res, DeepEquals, help.Ns(notification1, notification2))
65}68}
6669
67func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshotWithExpiration(c *C) {70func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshotWithExpiration(c *C) {
@@ -81,5 +84,5 @@
81 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)84 top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId)
82 c.Assert(err, IsNil)85 c.Assert(err, IsNil)
83 c.Check(top, Equals, int64(2))86 c.Check(top, Equals, int64(2))
84 c.Check(res, DeepEquals, []json.RawMessage{notification1})87 c.Check(res, DeepEquals, help.Ns(notification1))
85}88}
8689
=== modified file 'server/store/store.go'
--- server/store/store.go 2014-02-18 13:43:07 +0000
+++ server/store/store.go 2014-04-29 18:02:00 +0000
@@ -22,6 +22,8 @@
22 "encoding/json"22 "encoding/json"
23 "errors"23 "errors"
24 "time"24 "time"
25
26 "launchpad.net/ubuntu-push/protocol"
25)27)
2628
27type InternalChannelId string29type InternalChannelId string
@@ -70,7 +72,7 @@
70 AppendToChannel(chanId InternalChannelId, notification json.RawMessage, expiration time.Time) error72 AppendToChannel(chanId InternalChannelId, notification json.RawMessage, expiration time.Time) error
71 // GetChannelSnapshot gets all the current notifications and73 // GetChannelSnapshot gets all the current notifications and
72 // current top level in the channel.74 // current top level in the channel.
73 GetChannelSnapshot(chanId InternalChannelId) (topLevel int64, payloads []json.RawMessage, err error)75 GetChannelSnapshot(chanId InternalChannelId) (topLevel int64, notifications []protocol.Notification, err error)
74 // Close is to be called when done with the store.76 // Close is to be called when done with the store.
75 Close()77 Close()
76}78}
7779
=== modified file 'testing/helpers.go'
--- testing/helpers.go 2014-02-21 16:04:44 +0000
+++ testing/helpers.go 2014-04-29 18:02:00 +0000
@@ -18,6 +18,7 @@
18package testing18package testing
1919
20import (20import (
21 "encoding/json"
21 "fmt"22 "fmt"
22 "os"23 "os"
23 "path/filepath"24 "path/filepath"
@@ -26,6 +27,7 @@
26 "sync"27 "sync"
2728
28 "launchpad.net/ubuntu-push/logger"29 "launchpad.net/ubuntu-push/logger"
30 "launchpad.net/ubuntu-push/protocol"
29)31)
3032
31type captureHelper struct {33type captureHelper struct {
@@ -122,3 +124,12 @@
122 }124 }
123 return filepath.Join(dir, relativePath)125 return filepath.Join(dir, relativePath)
124}126}
127
128// Ns makes a []Notification from just payloads.
129func Ns(payloads ...json.RawMessage) []protocol.Notification {
130 res := make([]protocol.Notification, len(payloads))
131 for i := 0; i < len(payloads); i++ {
132 res[i].Payload = payloads[i]
133 }
134 return res
135}
125136
=== modified file 'ubuntu-push-client.go'
--- ubuntu-push-client.go 2014-03-12 13:25:20 +0000
+++ ubuntu-push-client.go 2014-04-29 18:02:00 +0000
@@ -19,12 +19,38 @@
19import (19import (
20 "log"20 "log"
2121
22 "gopkg.in/qml.v0"
23 "launchpad.net/go-dbus/v1"
22 "launchpad.net/go-xdg/v0"24 "launchpad.net/go-xdg/v0"
2325
24 "launchpad.net/ubuntu-push/client"26 "launchpad.net/ubuntu-push/client"
25)27)
2628
29const NAME = "com.ubuntu.PushNotifications"
30
31// grabName grabs ownership of the dbus name, and bails the client as
32// soon as somebody else grabs it.
33func grabName() {
34 conn, err := dbus.Connect(dbus.SessionBus)
35 if err != nil {
36 log.Fatalf("bus: %v", err)
37 }
38
39 flags := dbus.NameFlagAllowReplacement | dbus.NameFlagReplaceExisting
40 n := conn.RequestName(NAME, flags)
41 go func() {
42 for err := range n.C {
43 if err != nil {
44 log.Fatalf("FATAL: name channel got: %v", err)
45 }
46 }
47 }()
48}
49
27func main() {50func main() {
51 // XXX: this is a quick hack to ensure unicity
52 grabName()
53
28 cfgFname, err := xdg.Config.Find("ubuntu-push-client/config.json")54 cfgFname, err := xdg.Config.Find("ubuntu-push-client/config.json")
29 if err != nil {55 if err != nil {
30 log.Fatalf("unable to find a configuration file: %v", err)56 log.Fatalf("unable to find a configuration file: %v", err)
@@ -33,6 +59,9 @@
33 if err != nil {59 if err != nil {
34 log.Fatalf("unable to open the levels database: %v", err)60 log.Fatalf("unable to open the levels database: %v", err)
35 }61 }
62
63 qml.Init(nil)
64
36 cli := client.NewPushClient(cfgFname, lvlFname)65 cli := client.NewPushClient(cfgFname, lvlFname)
37 err = cli.Start()66 err = cli.Start()
38 if err != nil {67 if err != nil {
3968
=== added file 'util/auth.go'
--- util/auth.go 1970-01-01 00:00:00 +0000
+++ util/auth.go 2014-04-29 18:02:00 +0000
@@ -0,0 +1,36 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package util
18
19import (
20 "gopkg.in/niemeyer/uoneauth.v1"
21 "gopkg.in/qml.v0"
22)
23
24func GetAuthorization() (string, error) {
25 engine := qml.NewEngine()
26 defer engine.Destroy()
27 authService := uoneauth.NewService(engine)
28 var auth string
29 token, err := authService.Token()
30 if err != nil {
31 return "", err
32 } else {
33 auth = token.HeaderSignature("POST", "https://push.ubuntu.com")
34 }
35 return auth, nil
36}
037
=== added file 'util/auth_test.go'
--- util/auth_test.go 1970-01-01 00:00:00 +0000
+++ util/auth_test.go 2014-04-29 18:02:00 +0000
@@ -0,0 +1,53 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package util
18
19import (
20 "os"
21
22 "gopkg.in/qml.v0"
23
24 . "launchpad.net/gocheck"
25)
26
27type authSuite struct{}
28
29var _ = Suite(&authSuite{})
30
31func (s *authSuite) SetUpSuite(c *C) {
32 if os.Getenv("PUSH_AUTH_TEST") == "1" {
33 qml.Init(nil)
34 }
35}
36
37func (s *authSuite) SetUpTest(c *C) {
38 qml.SetLogger(c)
39}
40
41func (s *authSuite) TestGetAuth(c *C) {
42 /*
43 * This test is only useful when the PUSH_AUTH_TEST environment
44 * variable is set to "1" - in which case the runner should have
45 * a Ubuntu One account setup via system-settings.
46 */
47 if os.Getenv("PUSH_AUTH_TEST") != "1" {
48 c.Skip("PUSH_AUTH_TEST not set to '1'")
49 }
50 auth, err := GetAuthorization()
51 c.Assert(err, IsNil)
52 c.Assert(auth, Matches, "OAuth .*oauth_consumer_key=.*")
53}
054
=== modified file 'whoopsie/identifier/identifier.go'
--- whoopsie/identifier/identifier.go 2014-02-21 16:17:28 +0000
+++ whoopsie/identifier/identifier.go 2014-04-29 18:02:00 +0000
@@ -27,6 +27,7 @@
27import "C"27import "C"
28import "unsafe"28import "unsafe"
29import "errors"29import "errors"
30import "time"
3031
31// an Id knows how to generate itself, and how to stringify itself.32// an Id knows how to generate itself, and how to stringify itself.
32type Id interface {33type Id interface {
@@ -36,12 +37,17 @@
3637
37// Identifier is the default Id implementation.38// Identifier is the default Id implementation.
38type Identifier struct {39type Identifier struct {
39 value string40 value string
41 generator func(**C.char, **C.GError)
42}
43
44func generator(csp **C.char, errp **C.GError) {
45 C.whoopsie_identifier_generate(csp, errp)
40}46}
4147
42// New creates an Identifier, but does not call Generate() on it.48// New creates an Identifier, but does not call Generate() on it.
43func New() Id {49func New() Id {
44 return &Identifier{}50 return &Identifier{generator: generator}
45}51}
4652
47// Generate makes the Identifier create the identifier itself.53// Generate makes the Identifier create the identifier itself.
@@ -49,8 +55,18 @@
49 var gerr *C.GError55 var gerr *C.GError
50 var cs *C.char56 var cs *C.char
51 defer C.g_free((C.gpointer)(unsafe.Pointer(cs)))57 defer C.g_free((C.gpointer)(unsafe.Pointer(cs)))
52 C.whoopsie_identifier_generate(&cs, &gerr)58
5359 for i := 0; i < 200; i++ {
60 id.generator(&cs, &gerr)
61
62 if cs != nil || gerr != nil {
63 goto SuccessMaybe
64 }
65 time.Sleep(600 * time.Millisecond)
66 }
67 return errors.New("whoopsie_identifier_generate still bad after 2m; giving up")
68
69SuccessMaybe:
54 if gerr != nil {70 if gerr != nil {
55 return errors.New(C.GoString((*C.char)(gerr.message)))71 return errors.New(C.GoString((*C.char)(gerr.message)))
56 } else {72 } else {
5773
=== modified file 'whoopsie/identifier/identifier_test.go'
--- whoopsie/identifier/identifier_test.go 2014-01-15 15:51:50 +0000
+++ whoopsie/identifier/identifier_test.go 2014-04-29 18:02:00 +0000
@@ -41,3 +41,18 @@
41func (s *IdentifierSuite) TestIdentifierInterface(c *C) {41func (s *IdentifierSuite) TestIdentifierInterface(c *C) {
42 _ = []Id{New()}42 _ = []Id{New()}
43}43}
44
45// TestFailure checks that Identifier survives whoopsie shenanigans
46func (s *IdentifierSuite) TestIdentifierSurvivesShenanigans(c *C) {
47 count := 0
48 // using _Ctype* as a workaround for gocheck also having a C
49 gen := func(csp **_Ctype_char, errp **_Ctype_GError) {
50 count++
51 if count > 3 {
52 generator(csp, errp)
53 }
54 }
55 id := &Identifier{generator: gen}
56 id.Generate()
57 c.Check(id.String(), HasLen, 128)
58}

Subscribers

People subscribed via source and target branches