Merge lp:~pedronis/ubuntu-push/unicast-preps into lp:ubuntu-push
- unicast-preps
- Merge into trunk
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 |
Related bugs: |
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
1 | === modified file 'Makefile' |
2 | --- Makefile 2014-03-31 17:58:54 +0000 |
3 | +++ Makefile 2014-04-29 18:02:00 +0000 |
4 | @@ -11,10 +11,13 @@ |
5 | GODEPS += launchpad.net/go-dbus/v1 |
6 | GODEPS += launchpad.net/go-xdg/v0 |
7 | GODEPS += code.google.com/p/gosqlite/sqlite3 |
8 | +GODEPS += gopkg.in/qml.v0 |
9 | +GODEPS += gopkg.in/niemeyer/uoneauth.v1 |
10 | |
11 | TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client ) |
12 | |
13 | bootstrap: |
14 | + $(RM) -r $(GOPATH)/pkg |
15 | mkdir -p $(GOPATH)/bin |
16 | mkdir -p $(GOPATH)/pkg |
17 | go get -u launchpad.net/godeps |
18 | |
19 | === modified file 'README' |
20 | --- README 2014-03-31 17:58:54 +0000 |
21 | +++ README 2014-04-29 18:02:00 +0000 |
22 | @@ -6,11 +6,21 @@ |
23 | The code expects to be checked out as launchpad.net/ubuntu-push in a Go |
24 | workspace, see "go help gopath". |
25 | |
26 | -To setup Go dependencies, install libsqlite3-dev and run: |
27 | +To setup Go dependencies, install the following dependencies: |
28 | + |
29 | + build-essential |
30 | + libsqlite3-dev |
31 | + qtbase5-private-dev |
32 | + qtdeclarative5-dev |
33 | + libqt5opengl5-dev |
34 | + libubuntuoneauth-2.0-dev |
35 | + |
36 | +and run: |
37 | |
38 | make bootstrap |
39 | |
40 | -To run tests, install libgcrypt11-dev and libwhoopsie-dev and run: |
41 | +To run tests, install libglib2.0-dev, libgcrypt11-dev, libwhoopsie-dev, |
42 | +and run: |
43 | |
44 | make check |
45 | |
46 | |
47 | === modified file 'bus/connectivity/connectivity.go' |
48 | --- bus/connectivity/connectivity.go 2014-04-04 11:08:28 +0000 |
49 | +++ bus/connectivity/connectivity.go 2014-04-29 18:02:00 +0000 |
50 | @@ -72,19 +72,20 @@ |
51 | cs.connAttempts += ar.Redial() |
52 | nm := networkmanager.New(cs.endp, cs.log) |
53 | |
54 | + // set up the watch |
55 | + stateCh, err = nm.WatchState() |
56 | + if err != nil { |
57 | + cs.log.Debugf("failed to set up the state watch: %s", err) |
58 | + goto Continue |
59 | + } |
60 | + |
61 | // Get the current state. |
62 | initial = nm.GetState() |
63 | if initial == networkmanager.Unknown { |
64 | cs.log.Debugf("Failed to get state.") |
65 | goto Continue |
66 | } |
67 | - |
68 | - // set up the watch |
69 | - stateCh, err = nm.WatchState() |
70 | - if err != nil { |
71 | - cs.log.Debugf("failed to set up the state watch: %s", err) |
72 | - goto Continue |
73 | - } |
74 | + cs.log.Debugf("got initial state of %s", initial) |
75 | |
76 | primary = nm.GetPrimaryConnection() |
77 | cs.log.Debugf("primary connection starts as %#v", primary) |
78 | |
79 | === modified file 'bus/connectivity/connectivity_test.go' |
80 | --- bus/connectivity/connectivity_test.go 2014-04-04 12:01:42 +0000 |
81 | +++ bus/connectivity/connectivity_test.go 2014-04-29 18:02:00 +0000 |
82 | @@ -17,8 +17,15 @@ |
83 | package connectivity |
84 | |
85 | import ( |
86 | + "net/http/httptest" |
87 | + "sync" |
88 | + "testing" |
89 | + "time" |
90 | + |
91 | "launchpad.net/go-dbus/v1" |
92 | . "launchpad.net/gocheck" |
93 | + |
94 | + "launchpad.net/ubuntu-push/bus" |
95 | "launchpad.net/ubuntu-push/bus/networkmanager" |
96 | testingbus "launchpad.net/ubuntu-push/bus/testing" |
97 | "launchpad.net/ubuntu-push/config" |
98 | @@ -26,9 +33,6 @@ |
99 | helpers "launchpad.net/ubuntu-push/testing" |
100 | "launchpad.net/ubuntu-push/testing/condition" |
101 | "launchpad.net/ubuntu-push/util" |
102 | - "net/http/httptest" |
103 | - "testing" |
104 | - "time" |
105 | ) |
106 | |
107 | // hook up gocheck |
108 | @@ -115,6 +119,76 @@ |
109 | c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal) |
110 | } |
111 | |
112 | +// a racyEndpoint is an endpoint that behaves differently depending on |
113 | +// how much time passes between getting the state and setting up the |
114 | +// watch |
115 | +type racyEndpoint struct { |
116 | + stateGot bool |
117 | + maxTime time.Time |
118 | + delta time.Duration |
119 | + lock sync.RWMutex |
120 | +} |
121 | + |
122 | +func (rep *racyEndpoint) GetProperty(prop string) (interface{}, error) { |
123 | + switch prop { |
124 | + case "state": |
125 | + rep.lock.Lock() |
126 | + defer rep.lock.Unlock() |
127 | + rep.stateGot = true |
128 | + rep.maxTime = time.Now().Add(rep.delta) |
129 | + return uint32(networkmanager.Connecting), nil |
130 | + case "PrimaryConnection": |
131 | + return dbus.ObjectPath("/something"), nil |
132 | + default: |
133 | + return nil, nil |
134 | + } |
135 | +} |
136 | + |
137 | +func (rep *racyEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error { |
138 | + if member == "StateChanged" { |
139 | + // we count never having gotten the state as happening "after" now. |
140 | + rep.lock.RLock() |
141 | + defer rep.lock.RUnlock() |
142 | + ok := !rep.stateGot || time.Now().Before(rep.maxTime) |
143 | + go func() { |
144 | + if ok { |
145 | + f(uint32(networkmanager.ConnectedGlobal)) |
146 | + } |
147 | + d() |
148 | + }() |
149 | + } |
150 | + return nil |
151 | +} |
152 | + |
153 | +func (*racyEndpoint) Close() {} |
154 | +func (*racyEndpoint) Dial() error { return nil } |
155 | +func (*racyEndpoint) String() string { return "racyEndpoint" } |
156 | +func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil } |
157 | + |
158 | +var _ bus.Endpoint = (*racyEndpoint)(nil) |
159 | + |
160 | +// takeNext takes a value from given channel with a 1s timeout |
161 | +func takeNext(ch <-chan networkmanager.State) networkmanager.State { |
162 | + select { |
163 | + case <-time.After(time.Second): |
164 | + panic("channel stuck: too long waiting") |
165 | + case v := <-ch: |
166 | + return v |
167 | + } |
168 | +} |
169 | + |
170 | +// test that if the nm state goes from connecting to connected very |
171 | +// shortly after calling GetState, we don't lose the event. |
172 | +func (s *ConnSuite) TestStartAvoidsRace(c *C) { |
173 | + for delta := time.Second; delta > 1; delta /= 2 { |
174 | + rep := &racyEndpoint{delta: delta} |
175 | + cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: rep} |
176 | + f := Commentf("when delta=%s", delta) |
177 | + c.Assert(cs.start(), Equals, networkmanager.Connecting, f) |
178 | + c.Assert(takeNext(cs.networkStateCh), Equals, networkmanager.ConnectedGlobal, f) |
179 | + } |
180 | +} |
181 | + |
182 | /* |
183 | tests for connectedStateStep() |
184 | */ |
185 | |
186 | === modified file 'client/client.go' |
187 | --- client/client.go 2014-04-11 16:37:48 +0000 |
188 | +++ client/client.go 2014-04-29 18:02:00 +0000 |
189 | @@ -26,6 +26,7 @@ |
190 | "os" |
191 | "strings" |
192 | |
193 | + "gopkg.in/qml.v0" |
194 | "launchpad.net/go-dbus/v1" |
195 | |
196 | "launchpad.net/ubuntu-push/bus" |
197 | @@ -57,7 +58,7 @@ |
198 | // The PEM-encoded server certificate |
199 | CertPEMFile string `json:"cert_pem_file"` |
200 | // The logging level (one of "debug", "info", "error") |
201 | - LogLevel string `json:"log_level"` |
202 | + LogLevel logger.ConfigLogLevel `json:"log_level"` |
203 | } |
204 | |
205 | // PushClient is the Ubuntu Push Notifications client-side daemon. |
206 | @@ -95,13 +96,13 @@ |
207 | |
208 | // configure loads its configuration, and sets it up. |
209 | func (client *PushClient) configure() error { |
210 | - f, err := os.Open(client.configPath) |
211 | + _, err := os.Stat(client.configPath) |
212 | if err != nil { |
213 | - return fmt.Errorf("opening config: %v", err) |
214 | + return fmt.Errorf("config: %v", err) |
215 | } |
216 | - err = config.ReadConfig(f, &client.config) |
217 | + err = config.ReadFiles(&client.config, client.configPath, "<flags>") |
218 | if err != nil { |
219 | - return fmt.Errorf("reading config: %v", err) |
220 | + return fmt.Errorf("config: %v", err) |
221 | } |
222 | // ignore spaces |
223 | client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1) |
224 | @@ -110,7 +111,8 @@ |
225 | } |
226 | |
227 | // later, we'll be specifying more logging options in the config file |
228 | - client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel) |
229 | + client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel.Level()) |
230 | + qml.SetLogger(client.log) |
231 | |
232 | // overridden for testing |
233 | client.idder = identifier.New() |
234 | @@ -285,9 +287,6 @@ |
235 | h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} |
236 | nots := notifications.Raw(client.notificationsEndp, client.log) |
237 | body := "Tap to open the system updater." |
238 | - if msg != nil { |
239 | - body = fmt.Sprintf("[%d] %s", msg.TopLevel, body) |
240 | - } |
241 | not_id, err := nots.Notify( |
242 | "ubuntu-push-client", // app name |
243 | uint32(0), // id |
244 | |
245 | === modified file 'client/client_test.go' |
246 | --- client/client_test.go 2014-04-11 16:21:45 +0000 |
247 | +++ client/client_test.go 2014-04-29 18:02:00 +0000 |
248 | @@ -19,10 +19,12 @@ |
249 | import ( |
250 | "encoding/json" |
251 | "errors" |
252 | + "flag" |
253 | "fmt" |
254 | "io/ioutil" |
255 | "net/http" |
256 | "net/http/httptest" |
257 | + "os" |
258 | "path/filepath" |
259 | "reflect" |
260 | "testing" |
261 | @@ -37,6 +39,7 @@ |
262 | testibus "launchpad.net/ubuntu-push/bus/testing" |
263 | "launchpad.net/ubuntu-push/client/session" |
264 | "launchpad.net/ubuntu-push/client/session/levelmap" |
265 | + "launchpad.net/ubuntu-push/config" |
266 | helpers "launchpad.net/ubuntu-push/testing" |
267 | "launchpad.net/ubuntu-push/testing/condition" |
268 | "launchpad.net/ubuntu-push/util" |
269 | @@ -79,6 +82,7 @@ |
270 | } |
271 | |
272 | func (cs *clientSuite) SetUpSuite(c *C) { |
273 | + config.IgnoreParsedFlags = true // because configure() uses <flags> |
274 | cs.timeouts = util.SwapTimeouts([]time.Duration{0}) |
275 | cs.leveldbPath = "" |
276 | } |
277 | @@ -142,6 +146,16 @@ |
278 | c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond)) |
279 | } |
280 | |
281 | +func (cs *clientSuite) TestConfigureWorksWithFlags(c *C) { |
282 | + flag.CommandLine = flag.NewFlagSet("client", flag.ContinueOnError) |
283 | + os.Args = []string{"client", "-addr", "foo:7777"} |
284 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
285 | + err := cli.configure() |
286 | + c.Assert(err, IsNil) |
287 | + c.Assert(cli.config, NotNil) |
288 | + c.Check(cli.config.Addr, Equals, "foo:7777") |
289 | +} |
290 | + |
291 | func (cs *clientSuite) TestConfigureSetsUpLog(c *C) { |
292 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
293 | c.Check(cli.log, IsNil) |
294 | @@ -163,7 +177,7 @@ |
295 | c.Check(cli.idder, IsNil) |
296 | err := cli.configure() |
297 | c.Assert(err, IsNil) |
298 | - c.Assert(cli.idder, DeepEquals, identifier.New()) |
299 | + c.Assert(cli.idder, FitsTypeOf, identifier.New()) |
300 | } |
301 | |
302 | func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) { |
303 | |
304 | === modified file 'client/gethosts/gethost.go' |
305 | --- client/gethosts/gethost.go 2014-03-24 15:32:29 +0000 |
306 | +++ client/gethosts/gethost.go 2014-04-29 18:02:00 +0000 |
307 | @@ -49,8 +49,10 @@ |
308 | } |
309 | } |
310 | |
311 | -type expected struct { |
312 | - Hosts []string |
313 | +// Host contains the domain and hosts returned by the remote endpoint |
314 | +type Host struct { |
315 | + Domain string |
316 | + Hosts []string |
317 | } |
318 | |
319 | var ( |
320 | @@ -60,7 +62,7 @@ |
321 | ) |
322 | |
323 | // Get gets a list of hosts consulting the endpoint. |
324 | -func (gh *GetHost) Get() ([]string, error) { |
325 | +func (gh *GetHost) Get() (*Host, error) { |
326 | resp, err := gh.cli.Get(gh.endpointUrl + "?h=" + gh.hash) |
327 | if err != nil { |
328 | return nil, err |
329 | @@ -80,7 +82,7 @@ |
330 | if err != nil { |
331 | return nil, err |
332 | } |
333 | - var parsed expected |
334 | + var parsed Host |
335 | err = json.Unmarshal(body, &parsed) |
336 | if err != nil { |
337 | return nil, ErrTemporary |
338 | @@ -88,5 +90,5 @@ |
339 | if len(parsed.Hosts) == 0 { |
340 | return nil, ErrTemporary |
341 | } |
342 | - return parsed.Hosts, nil |
343 | + return &parsed, nil |
344 | } |
345 | |
346 | === modified file 'client/gethosts/gethost_test.go' |
347 | --- client/gethosts/gethost_test.go 2014-03-31 14:31:07 +0000 |
348 | +++ client/gethosts/gethost_test.go 2014-04-29 18:02:00 +0000 |
349 | @@ -45,7 +45,8 @@ |
350 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
351 | x := r.FormValue("h") |
352 | b, err := json.Marshal(map[string]interface{}{ |
353 | - "hosts": []string{"http://" + x}, |
354 | + "domain": "example.com", |
355 | + "hosts": []string{"http://" + x}, |
356 | }) |
357 | if err != nil { |
358 | panic(err) |
359 | @@ -57,7 +58,8 @@ |
360 | gh := New("foobar", ts.URL, 1*time.Second) |
361 | res, err := gh.Get() |
362 | c.Assert(err, IsNil) |
363 | - c.Check(res, DeepEquals, []string{"http://c1130408a700afe0"}) |
364 | + c.Check(*res, DeepEquals, |
365 | + Host{Domain: "example.com", Hosts: []string{"http://c1130408a700afe0"}}) |
366 | } |
367 | |
368 | func (s *getHostsSuite) TestGetTimeout(c *C) { |
369 | @@ -97,4 +99,6 @@ |
370 | |
371 | scenario(http.StatusOK, "{", ErrTemporary) |
372 | scenario(http.StatusOK, "{}", ErrTemporary) |
373 | + scenario(http.StatusOK, `{"domain": "example.com"}`, ErrTemporary) |
374 | + scenario(http.StatusOK, `{"hosts": ["one"]}`, nil) |
375 | } |
376 | |
377 | === modified file 'client/session/session.go' |
378 | --- client/session/session.go 2014-04-04 13:55:00 +0000 |
379 | +++ client/session/session.go 2014-04-29 18:02:00 +0000 |
380 | @@ -38,7 +38,11 @@ |
381 | "launchpad.net/ubuntu-push/util" |
382 | ) |
383 | |
384 | -var wireVersionBytes = []byte{protocol.ProtocolWireVersion} |
385 | +var ( |
386 | + wireVersionBytes = []byte{protocol.ProtocolWireVersion} |
387 | + getAuthorization = util.GetAuthorization |
388 | + shouldGetAuth = false |
389 | +) |
390 | |
391 | type Notification struct { |
392 | TopLevel int64 |
393 | @@ -73,7 +77,7 @@ |
394 | ) |
395 | |
396 | type hostGetter interface { |
397 | - Get() ([]string, error) |
398 | + Get() (*gethosts.Host, error) |
399 | } |
400 | |
401 | // ClientSessionConfig groups the client session configuration. |
402 | @@ -115,6 +119,28 @@ |
403 | stateP *uint32 |
404 | ErrCh chan error |
405 | MsgCh chan *Notification |
406 | + // authorization |
407 | + auth string |
408 | + // autoredial knobs |
409 | + shouldDelayP *uint32 |
410 | + lastAutoRedial time.Time |
411 | + redialDelay func(*ClientSession) time.Duration |
412 | + redialJitter func(time.Duration) time.Duration |
413 | + redialDelays []time.Duration |
414 | + redialDelaysIdx int |
415 | +} |
416 | + |
417 | +func redialDelay(sess *ClientSession) time.Duration { |
418 | + if sess.ShouldDelay() { |
419 | + t := sess.redialDelays[sess.redialDelaysIdx] |
420 | + if len(sess.redialDelays) > sess.redialDelaysIdx+1 { |
421 | + sess.redialDelaysIdx++ |
422 | + } |
423 | + return t + sess.redialJitter(t) |
424 | + } else { |
425 | + sess.redialDelaysIdx = 0 |
426 | + return 0 |
427 | + } |
428 | } |
429 | |
430 | func NewSession(serverAddrSpec string, conf ClientSessionConfig, |
431 | @@ -131,6 +157,7 @@ |
432 | if hostsEndpoint != "" { |
433 | getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout) |
434 | } |
435 | + var shouldDelay uint32 = 0 |
436 | sess := &ClientSession{ |
437 | ClientSessionConfig: conf, |
438 | getHost: getHost, |
439 | @@ -139,10 +166,14 @@ |
440 | Log: log, |
441 | Protocolator: protocol.NewProtocol0, |
442 | Levels: levels, |
443 | - TLS: &tls.Config{InsecureSkipVerify: true}, // XXX |
444 | + TLS: &tls.Config{}, |
445 | stateP: &state, |
446 | timeSince: time.Since, |
447 | + shouldDelayP: &shouldDelay, |
448 | + redialDelay: redialDelay, |
449 | + redialDelays: util.Timeouts(), |
450 | } |
451 | + sess.redialJitter = sess.Jitter |
452 | if sess.PEM != nil { |
453 | cp := x509.NewCertPool() |
454 | ok := cp.AppendCertsFromPEM(sess.PEM) |
455 | @@ -154,6 +185,18 @@ |
456 | return sess, nil |
457 | } |
458 | |
459 | +func (sess *ClientSession) ShouldDelay() bool { |
460 | + return atomic.LoadUint32(sess.shouldDelayP) != 0 |
461 | +} |
462 | + |
463 | +func (sess *ClientSession) setShouldDelay() { |
464 | + atomic.StoreUint32(sess.shouldDelayP, uint32(1)) |
465 | +} |
466 | + |
467 | +func (sess *ClientSession) clearShouldDelay() { |
468 | + atomic.StoreUint32(sess.shouldDelayP, uint32(0)) |
469 | +} |
470 | + |
471 | func (sess *ClientSession) State() ClientSessionState { |
472 | return ClientSessionState(atomic.LoadUint32(sess.stateP)) |
473 | } |
474 | @@ -180,20 +223,38 @@ |
475 | if sess.deliveryHosts != nil && sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime { |
476 | return nil |
477 | } |
478 | - hosts, err := sess.getHost.Get() |
479 | + host, err := sess.getHost.Get() |
480 | if err != nil { |
481 | sess.Log.Errorf("getHosts: %v", err) |
482 | sess.setState(Error) |
483 | return err |
484 | } |
485 | sess.deliveryHostsTimestamp = time.Now() |
486 | - sess.deliveryHosts = hosts |
487 | + sess.deliveryHosts = host.Hosts |
488 | + if sess.TLS != nil { |
489 | + sess.TLS.ServerName = host.Domain |
490 | + } |
491 | } else { |
492 | sess.deliveryHosts = sess.fallbackHosts |
493 | } |
494 | return nil |
495 | } |
496 | |
497 | +// checkAuthorization checks the authorization within the phone |
498 | +func (sess *ClientSession) checkAuthorization() error { |
499 | + // grab the authorization string from the accounts |
500 | + // TODO: remove this condition when we have a way to deal with failing authorizations |
501 | + if shouldGetAuth { |
502 | + auth, err := getAuthorization() |
503 | + if err != nil { |
504 | + // For now we just log the error, as we don't want to block unauthorized users |
505 | + sess.Log.Errorf("unable to get the authorization token from the account: %v", err) |
506 | + } |
507 | + sess.auth = auth |
508 | + } |
509 | + return nil |
510 | +} |
511 | + |
512 | func (sess *ClientSession) resetHosts() { |
513 | sess.deliveryHosts = nil |
514 | } |
515 | @@ -234,6 +295,7 @@ |
516 | // connect to a server using the configuration in the ClientSession |
517 | // and set up the connection. |
518 | func (sess *ClientSession) connect() error { |
519 | + sess.setShouldDelay() |
520 | sess.startConnectionAttempt() |
521 | var err error |
522 | var conn net.Conn |
523 | @@ -263,7 +325,12 @@ |
524 | |
525 | func (sess *ClientSession) AutoRedial(doneCh chan uint32) { |
526 | sess.stopRedial() |
527 | + if time.Since(sess.lastAutoRedial) < 2*time.Second { |
528 | + sess.setShouldDelay() |
529 | + } |
530 | + time.Sleep(sess.redialDelay(sess)) |
531 | sess.retrier = util.NewAutoRedialer(sess) |
532 | + sess.lastAutoRedial = time.Now() |
533 | go func() { doneCh <- sess.retrier.Redial() }() |
534 | } |
535 | |
536 | @@ -289,6 +356,7 @@ |
537 | err := sess.proto.WriteMessage(protocol.PingPongMsg{Type: "pong"}) |
538 | if err == nil { |
539 | sess.Log.Debugf("ping.") |
540 | + sess.clearShouldDelay() |
541 | } else { |
542 | sess.setState(Error) |
543 | sess.Log.Errorf("unable to pong: %s", err) |
544 | @@ -330,6 +398,7 @@ |
545 | sess.Log.Errorf("unable to ack broadcast: %s", err) |
546 | return err |
547 | } |
548 | + sess.clearShouldDelay() |
549 | sess.Log.Debugf("broadcast chan:%v app:%v topLevel:%d payloads:%s", |
550 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) |
551 | if bcast.ChanId == protocol.SystemChannelId { |
552 | @@ -409,10 +478,9 @@ |
553 | return err |
554 | } |
555 | err = proto.WriteMessage(protocol.ConnectMsg{ |
556 | - Type: "connect", |
557 | - DeviceId: sess.DeviceId, |
558 | - // xxx get the SSO Authorization string from the phone |
559 | - Authorization: "", |
560 | + Type: "connect", |
561 | + DeviceId: sess.DeviceId, |
562 | + Authorization: sess.auth, |
563 | Levels: levels, |
564 | Info: sess.Info, |
565 | }) |
566 | @@ -447,13 +515,15 @@ |
567 | |
568 | // run calls connect, and if it works it calls start, and if it works |
569 | // it runs loop in a goroutine, and ships its return value over ErrCh. |
570 | -func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error { |
571 | +func (sess *ClientSession) run(closer func(), authChecker, hostGetter, connecter, starter, looper func() error) error { |
572 | closer() |
573 | - err := hostGetter() |
574 | - if err != nil { |
575 | - return err |
576 | - } |
577 | - err = connecter() |
578 | + if err := authChecker(); err != nil { |
579 | + return err |
580 | + } |
581 | + if err := hostGetter(); err != nil { |
582 | + return err |
583 | + } |
584 | + err := connecter() |
585 | if err == nil { |
586 | err = starter() |
587 | if err == nil { |
588 | @@ -483,7 +553,7 @@ |
589 | // keep on trying. |
590 | panic("can't Dial() without a protocol constructor.") |
591 | } |
592 | - return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop) |
593 | + return sess.run(sess.doClose, sess.checkAuthorization, sess.getHosts, sess.connect, sess.start, sess.loop) |
594 | } |
595 | |
596 | func init() { |
597 | |
598 | === modified file 'client/session/session_test.go' |
599 | --- client/session/session_test.go 2014-04-04 13:55:00 +0000 |
600 | +++ client/session/session_test.go 2014-04-29 18:02:00 +0000 |
601 | @@ -32,12 +32,12 @@ |
602 | |
603 | . "launchpad.net/gocheck" |
604 | |
605 | + "launchpad.net/ubuntu-push/client/gethosts" |
606 | "launchpad.net/ubuntu-push/client/session/levelmap" |
607 | - //"launchpad.net/ubuntu-push/client/gethosts" |
608 | - "launchpad.net/ubuntu-push/logger" |
609 | "launchpad.net/ubuntu-push/protocol" |
610 | helpers "launchpad.net/ubuntu-push/testing" |
611 | "launchpad.net/ubuntu-push/testing/condition" |
612 | + "launchpad.net/ubuntu-push/util" |
613 | ) |
614 | |
615 | func TestSession(t *testing.T) { TestingT(t) } |
616 | @@ -165,14 +165,26 @@ |
617 | ///// |
618 | |
619 | type clientSessionSuite struct { |
620 | - log logger.Logger |
621 | + log *helpers.TestLogger |
622 | lvls func() (levelmap.LevelMap, error) |
623 | } |
624 | |
625 | +func (cs *clientSessionSuite) SetUpSuite(c *C) { |
626 | + getAuthorization = func() (string, error) { |
627 | + return "some auth", nil |
628 | + } |
629 | + shouldGetAuth = true |
630 | +} |
631 | + |
632 | func (cs *clientSessionSuite) SetUpTest(c *C) { |
633 | cs.log = helpers.NewTestLogger(c, "debug") |
634 | } |
635 | |
636 | +func (cs *clientSessionSuite) TearDownSuite(c *C) { |
637 | + getAuthorization = util.GetAuthorization |
638 | + shouldGetAuth = false |
639 | +} |
640 | + |
641 | // in-memory level map testing |
642 | var _ = Suite(&clientSessionSuite{lvls: levelmap.NewLevelMap}) |
643 | |
644 | @@ -182,6 +194,7 @@ |
645 | var _ = Suite(&clientSqlevelsSessionSuite{}) |
646 | |
647 | func (cs *clientSqlevelsSessionSuite) SetUpSuite(c *C) { |
648 | + cs.clientSessionSuite.SetUpSuite(c) |
649 | cs.lvls = func() (levelmap.LevelMap, error) { return levelmap.NewSqliteLevelMap(":memory:") } |
650 | } |
651 | |
652 | @@ -214,6 +227,10 @@ |
653 | c.Check(sess, NotNil) |
654 | c.Check(err, IsNil) |
655 | c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"}) |
656 | + // the session is happy and redial delayer is default |
657 | + c.Check(sess.ShouldDelay(), Equals, false) |
658 | + c.Check(fmt.Sprintf("%#v", sess.redialDelay), Equals, fmt.Sprintf("%#v", redialDelay)) |
659 | + c.Check(sess.redialDelays, DeepEquals, util.Timeouts()) |
660 | // but no root CAs set |
661 | c.Check(sess.TLS.RootCAs, IsNil) |
662 | c.Check(sess.State(), Equals, Disconnected) |
663 | @@ -264,16 +281,17 @@ |
664 | } |
665 | |
666 | type testHostGetter struct { |
667 | - hosts []string |
668 | - err error |
669 | + domain string |
670 | + hosts []string |
671 | + err error |
672 | } |
673 | |
674 | -func (thg *testHostGetter) Get() ([]string, error) { |
675 | - return thg.hosts, thg.err |
676 | +func (thg *testHostGetter) Get() (*gethosts.Host, error) { |
677 | + return &gethosts.Host{thg.domain, thg.hosts}, thg.err |
678 | } |
679 | |
680 | func (cs *clientSessionSuite) TestGetHostsRemote(c *C) { |
681 | - hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} |
682 | + hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
683 | sess := &ClientSession{getHost: hostGetter, timeSince: time.Since} |
684 | err := sess.getHosts() |
685 | c.Assert(err, IsNil) |
686 | @@ -284,7 +302,7 @@ |
687 | sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log) |
688 | c.Assert(err, IsNil) |
689 | hostsErr := errors.New("failed") |
690 | - hostGetter := &testHostGetter{nil, hostsErr} |
691 | + hostGetter := &testHostGetter{"", nil, hostsErr} |
692 | sess.getHost = hostGetter |
693 | err = sess.getHosts() |
694 | c.Assert(err, Equals, hostsErr) |
695 | @@ -293,7 +311,7 @@ |
696 | } |
697 | |
698 | func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) { |
699 | - hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} |
700 | + hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
701 | sess := &ClientSession{ |
702 | getHost: hostGetter, |
703 | ClientSessionConfig: ClientSessionConfig{ |
704 | @@ -318,7 +336,7 @@ |
705 | } |
706 | |
707 | func (cs *clientSessionSuite) TestGetHostsRemoteCachingReset(c *C) { |
708 | - hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} |
709 | + hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
710 | sess := &ClientSession{ |
711 | getHost: hostGetter, |
712 | ClientSessionConfig: ClientSessionConfig{ |
713 | @@ -341,6 +359,18 @@ |
714 | } |
715 | |
716 | /**************************************************************** |
717 | + checkAuthorization() tests |
718 | +****************************************************************/ |
719 | + |
720 | +func (cs *clientSessionSuite) TestChecksAuthorizationFromServer(c *C) { |
721 | + sess := &ClientSession{} |
722 | + c.Assert(sess.auth, Equals, "") |
723 | + err := sess.checkAuthorization() |
724 | + c.Assert(err, IsNil) |
725 | + c.Check(sess.auth, Equals, "some auth") |
726 | +} |
727 | + |
728 | +/**************************************************************** |
729 | startConnectionAttempt()/nextHostToTry()/started tests |
730 | ****************************************************************/ |
731 | |
732 | @@ -427,7 +457,9 @@ |
733 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
734 | c.Assert(err, IsNil) |
735 | sess.deliveryHosts = []string{"nowhere"} |
736 | + sess.clearShouldDelay() |
737 | err = sess.connect() |
738 | + c.Check(sess.ShouldDelay(), Equals, true) |
739 | c.Check(err, ErrorMatches, ".*connect.*address.*") |
740 | c.Check(sess.State(), Equals, Error) |
741 | } |
742 | @@ -439,7 +471,9 @@ |
743 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
744 | c.Assert(err, IsNil) |
745 | sess.deliveryHosts = []string{srv.Addr().String()} |
746 | + sess.clearShouldDelay() |
747 | err = sess.connect() |
748 | + c.Check(sess.ShouldDelay(), Equals, true) |
749 | c.Check(err, IsNil) |
750 | c.Check(sess.Connection, NotNil) |
751 | c.Check(sess.State(), Equals, Connected) |
752 | @@ -452,7 +486,9 @@ |
753 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
754 | c.Assert(err, IsNil) |
755 | sess.deliveryHosts = []string{"nowhere", srv.Addr().String()} |
756 | + sess.clearShouldDelay() |
757 | err = sess.connect() |
758 | + c.Check(sess.ShouldDelay(), Equals, true) |
759 | c.Check(err, IsNil) |
760 | c.Check(sess.Connection, NotNil) |
761 | c.Check(sess.State(), Equals, Connected) |
762 | @@ -466,7 +502,9 @@ |
763 | srv.Close() |
764 | c.Assert(err, IsNil) |
765 | sess.deliveryHosts = []string{srv.Addr().String()} |
766 | + sess.clearShouldDelay() |
767 | err = sess.connect() |
768 | + c.Check(sess.ShouldDelay(), Equals, true) |
769 | c.Check(err, ErrorMatches, ".*connection refused") |
770 | c.Check(sess.State(), Equals, Error) |
771 | } |
772 | @@ -548,6 +586,27 @@ |
773 | c.Check(<-ch, Not(Equals), 0) |
774 | } |
775 | |
776 | +func (cs *clientSessionSuite) TestAutoRedialCallsRedialDelay(c *C) { |
777 | + sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
778 | + c.Assert(err, IsNil) |
779 | + flag := false |
780 | + sess.redialDelay = func(sess *ClientSession) time.Duration { flag = true; return 0 } |
781 | + sess.AutoRedial(nil) |
782 | + c.Check(flag, Equals, true) |
783 | +} |
784 | + |
785 | +func (cs *clientSessionSuite) TestAutoRedialSetsRedialDelayIfTooQuick(c *C) { |
786 | + sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
787 | + c.Assert(err, IsNil) |
788 | + sess.redialDelay = func(sess *ClientSession) time.Duration { return 0 } |
789 | + sess.AutoRedial(nil) |
790 | + c.Check(sess.ShouldDelay(), Equals, false) |
791 | + sess.stopRedial() |
792 | + sess.clearShouldDelay() |
793 | + sess.AutoRedial(nil) |
794 | + c.Check(sess.ShouldDelay(), Equals, true) |
795 | +} |
796 | + |
797 | /**************************************************************** |
798 | handlePing() tests |
799 | ****************************************************************/ |
800 | @@ -594,6 +653,24 @@ |
801 | c.Check(s.sess.State(), Equals, Error) |
802 | } |
803 | |
804 | +func (s *msgSuite) TestHandlePingClearsDelay(c *C) { |
805 | + s.sess.setShouldDelay() |
806 | + s.upCh <- nil // no error |
807 | + c.Check(s.sess.handlePing(), IsNil) |
808 | + c.Assert(len(s.downCh), Equals, 1) |
809 | + c.Check(<-s.downCh, Equals, protocol.PingPongMsg{Type: "pong"}) |
810 | + c.Check(s.sess.ShouldDelay(), Equals, false) |
811 | +} |
812 | + |
813 | +func (s *msgSuite) TestHandlePingDoesNotClearsDelayOnError(c *C) { |
814 | + s.sess.setShouldDelay() |
815 | + s.upCh <- errors.New("Pong") |
816 | + c.Check(s.sess.handlePing(), NotNil) |
817 | + c.Assert(len(s.downCh), Equals, 1) |
818 | + c.Check(<-s.downCh, Equals, protocol.PingPongMsg{Type: "pong"}) |
819 | + c.Check(s.sess.ShouldDelay(), Equals, true) |
820 | +} |
821 | + |
822 | /**************************************************************** |
823 | handleBroadcast() tests |
824 | ****************************************************************/ |
825 | @@ -687,6 +764,32 @@ |
826 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"nak"}) |
827 | } |
828 | |
829 | +func (s *msgSuite) TestHandleBroadcastClearsDelay(c *C) { |
830 | + s.sess.setShouldDelay() |
831 | + |
832 | + msg := serverMsg{"broadcast", protocol.BroadcastMsg{}, |
833 | + protocol.NotificationsMsg{}, protocol.ConnBrokenMsg{}} |
834 | + go func() { s.errCh <- s.sess.handleBroadcast(&msg) }() |
835 | + c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
836 | + s.upCh <- nil // ack ok |
837 | + c.Check(<-s.errCh, IsNil) |
838 | + |
839 | + c.Check(s.sess.ShouldDelay(), Equals, false) |
840 | +} |
841 | + |
842 | +func (s *msgSuite) TestHandleBroadcastDoesNotClearDelayOnError(c *C) { |
843 | + s.sess.setShouldDelay() |
844 | + |
845 | + msg := serverMsg{"broadcast", protocol.BroadcastMsg{}, |
846 | + protocol.NotificationsMsg{}, protocol.ConnBrokenMsg{}} |
847 | + go func() { s.errCh <- s.sess.handleBroadcast(&msg) }() |
848 | + c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
849 | + s.upCh <- errors.New("bcast") |
850 | + c.Check(<-s.errCh, NotNil) |
851 | + |
852 | + c.Check(s.sess.ShouldDelay(), Equals, true) |
853 | +} |
854 | + |
855 | /**************************************************************** |
856 | handleConnBroken() tests |
857 | ****************************************************************/ |
858 | @@ -852,9 +955,10 @@ |
859 | |
860 | c.Check(takeNext(downCh), Equals, "deadline 0") |
861 | c.Check(takeNext(downCh), DeepEquals, protocol.ConnectMsg{ |
862 | - Type: "connect", |
863 | - DeviceId: sess.DeviceId, |
864 | - Levels: map[string]int64{}, |
865 | + Type: "connect", |
866 | + DeviceId: sess.DeviceId, |
867 | + Levels: map[string]int64{}, |
868 | + Authorization: "", |
869 | }) |
870 | upCh <- errors.New("Overflow error in /dev/null") |
871 | err = <-errCh |
872 | @@ -959,6 +1063,7 @@ |
873 | msg, ok := takeNext(downCh).(protocol.ConnectMsg) |
874 | c.Check(ok, Equals, true) |
875 | c.Check(msg.DeviceId, Equals, "wah") |
876 | + c.Check(msg.Authorization, Equals, "") |
877 | c.Check(msg.Info, DeepEquals, info) |
878 | upCh <- nil // no error |
879 | upCh <- protocol.ConnAckMsg{ |
880 | @@ -975,6 +1080,22 @@ |
881 | run() tests |
882 | ****************************************************************/ |
883 | |
884 | +func (cs *clientSessionSuite) TestRunBailsIfAuthCheckFails(c *C) { |
885 | + sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
886 | + c.Assert(err, IsNil) |
887 | + failure := errors.New("TestRunBailsIfAuthCheckFails") |
888 | + has_closed := false |
889 | + err = sess.run( |
890 | + func() { has_closed = true }, |
891 | + func() error { return failure }, |
892 | + nil, |
893 | + nil, |
894 | + nil, |
895 | + nil) |
896 | + c.Check(err, Equals, failure) |
897 | + c.Check(has_closed, Equals, true) |
898 | +} |
899 | + |
900 | func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) { |
901 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
902 | c.Assert(err, IsNil) |
903 | @@ -982,6 +1103,7 @@ |
904 | has_closed := false |
905 | err = sess.run( |
906 | func() { has_closed = true }, |
907 | + func() error { return nil }, |
908 | func() error { return failure }, |
909 | nil, |
910 | nil, |
911 | @@ -997,6 +1119,7 @@ |
912 | err = sess.run( |
913 | func() {}, |
914 | func() error { return nil }, |
915 | + func() error { return nil }, |
916 | func() error { return failure }, |
917 | nil, |
918 | nil) |
919 | @@ -1011,6 +1134,7 @@ |
920 | func() {}, |
921 | func() error { return nil }, |
922 | func() error { return nil }, |
923 | + func() error { return nil }, |
924 | func() error { return failure }, |
925 | nil) |
926 | c.Check(err, Equals, failure) |
927 | @@ -1030,6 +1154,7 @@ |
928 | func() error { return nil }, |
929 | func() error { return nil }, |
930 | func() error { return nil }, |
931 | + func() error { return nil }, |
932 | func() error { sess.MsgCh <- notf; return <-failureCh }) |
933 | c.Check(err, Equals, nil) |
934 | // if run doesn't error it sets up the channels |
935 | @@ -1087,9 +1212,64 @@ |
936 | |
937 | var ( |
938 | dialTestTimeout = 100 * time.Millisecond |
939 | - dialTestConf = ClientSessionConfig{ExchangeTimeout: dialTestTimeout} |
940 | + dialTestConf = ClientSessionConfig{ |
941 | + ExchangeTimeout: dialTestTimeout, |
942 | + PEM: helpers.TestCertPEMBlock, |
943 | + } |
944 | ) |
945 | |
946 | +func (cs *clientSessionSuite) TestDialBadServerName(c *C) { |
947 | + // a borked server name |
948 | + cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) |
949 | + c.Assert(err, IsNil) |
950 | + tlsCfg := &tls.Config{ |
951 | + Certificates: []tls.Certificate{cert}, |
952 | + SessionTicketsDisabled: true, |
953 | + } |
954 | + |
955 | + lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) |
956 | + c.Assert(err, IsNil) |
957 | + // advertise |
958 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
959 | + b, err := json.Marshal(map[string]interface{}{ |
960 | + "domain": "xyzzy", // <-- *** THIS *** is the bit that'll break it |
961 | + "hosts": []string{"nowhere", lst.Addr().String()}, |
962 | + }) |
963 | + if err != nil { |
964 | + panic(err) |
965 | + } |
966 | + w.Header().Set("Content-Type", "application/json") |
967 | + w.Write(b) |
968 | + })) |
969 | + defer ts.Close() |
970 | + |
971 | + sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log) |
972 | + c.Assert(err, IsNil) |
973 | + tconn := &testConn{} |
974 | + sess.Connection = tconn |
975 | + |
976 | + upCh := make(chan interface{}, 5) |
977 | + downCh := make(chan interface{}, 5) |
978 | + errCh := make(chan error, 1) |
979 | + proto := &testProtocol{up: upCh, down: downCh} |
980 | + sess.Protocolator = func(net.Conn) protocol.Protocol { return proto } |
981 | + |
982 | + go func() { |
983 | + errCh <- sess.Dial() |
984 | + }() |
985 | + |
986 | + srv, err := lst.Accept() |
987 | + c.Assert(err, IsNil) |
988 | + |
989 | + // connect done |
990 | + |
991 | + _, err = protocol.ReadWireFormatVersion(srv, dialTestTimeout) |
992 | + c.Check(err, NotNil) |
993 | + |
994 | + c.Check(<-errCh, NotNil) |
995 | + c.Check(sess.State(), Equals, Error) |
996 | +} |
997 | + |
998 | func (cs *clientSessionSuite) TestDialWorks(c *C) { |
999 | // happy path thoughts |
1000 | cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) |
1001 | @@ -1104,7 +1284,8 @@ |
1002 | // advertise |
1003 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
1004 | b, err := json.Marshal(map[string]interface{}{ |
1005 | - "hosts": []string{"nowhere", lst.Addr().String()}, |
1006 | + "domain": "localhost", |
1007 | + "hosts": []string{"nowhere", lst.Addr().String()}, |
1008 | }) |
1009 | if err != nil { |
1010 | panic(err) |
1011 | @@ -1223,3 +1404,38 @@ |
1012 | c.Assert(err, IsNil) |
1013 | // connect done |
1014 | } |
1015 | + |
1016 | +/**************************************************************** |
1017 | + redialDelay() tests |
1018 | +****************************************************************/ |
1019 | + |
1020 | +func (cs *clientSessionSuite) TestShouldDelay(c *C) { |
1021 | + sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) |
1022 | + c.Assert(err, IsNil) |
1023 | + c.Check(sess.ShouldDelay(), Equals, false) |
1024 | + sess.setShouldDelay() |
1025 | + c.Check(sess.ShouldDelay(), Equals, true) |
1026 | + sess.clearShouldDelay() |
1027 | + c.Check(sess.ShouldDelay(), Equals, false) |
1028 | +} |
1029 | + |
1030 | +func (cs *clientSessionSuite) TestRedialDelay(c *C) { |
1031 | + sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) |
1032 | + c.Assert(err, IsNil) |
1033 | + sess.redialDelays = []time.Duration{17, 42} |
1034 | + n := 0 |
1035 | + sess.redialJitter = func(time.Duration) time.Duration { n++; return 0 } |
1036 | + // we get increasing delays while we're unhappy |
1037 | + sess.setShouldDelay() |
1038 | + c.Check(redialDelay(sess), Equals, time.Duration(17)) |
1039 | + c.Check(redialDelay(sess), Equals, time.Duration(42)) |
1040 | + c.Check(redialDelay(sess), Equals, time.Duration(42)) |
1041 | + // once we're happy, delays drop to 0 |
1042 | + sess.clearShouldDelay() |
1043 | + c.Check(redialDelay(sess), Equals, time.Duration(0)) |
1044 | + // and start again from the top if we become unhappy again |
1045 | + sess.setShouldDelay() |
1046 | + c.Check(redialDelay(sess), Equals, time.Duration(17)) |
1047 | + // and redialJitter got called every time shouldDelay was true |
1048 | + c.Check(n, Equals, 4) |
1049 | +} |
1050 | |
1051 | === modified file 'config/config.go' |
1052 | --- config/config.go 2014-03-25 18:49:18 +0000 |
1053 | +++ config/config.go 2014-04-29 18:02:00 +0000 |
1054 | @@ -20,6 +20,7 @@ |
1055 | import ( |
1056 | "encoding/json" |
1057 | "errors" |
1058 | + "flag" |
1059 | "fmt" |
1060 | "io" |
1061 | "io/ioutil" |
1062 | @@ -27,6 +28,7 @@ |
1063 | "os" |
1064 | "path/filepath" |
1065 | "reflect" |
1066 | + "strconv" |
1067 | "strings" |
1068 | "time" |
1069 | ) |
1070 | @@ -118,6 +120,22 @@ |
1071 | return fillDestConfig(destValue, p1) |
1072 | } |
1073 | |
1074 | +// FromString are config holders that can be set by parsing a string. |
1075 | +type FromString interface { |
1076 | + SetFromString(enc string) error |
1077 | +} |
1078 | + |
1079 | +// UnmarshalJSONViaString helps unmarshalling from JSON for FromString |
1080 | +// supporting config holders. |
1081 | +func UnmarshalJSONViaString(dest FromString, b []byte) error { |
1082 | + var enc string |
1083 | + err := json.Unmarshal(b, &enc) |
1084 | + if err != nil { |
1085 | + return err |
1086 | + } |
1087 | + return dest.SetFromString(enc) |
1088 | +} |
1089 | + |
1090 | // ConfigTimeDuration can hold a time.Duration in a configuration struct, |
1091 | // that is parsed from a string as supported by time.ParseDuration. |
1092 | type ConfigTimeDuration struct { |
1093 | @@ -125,13 +143,11 @@ |
1094 | } |
1095 | |
1096 | func (ctd *ConfigTimeDuration) UnmarshalJSON(b []byte) error { |
1097 | - var enc string |
1098 | - var v time.Duration |
1099 | - err := json.Unmarshal(b, &enc) |
1100 | - if err != nil { |
1101 | - return err |
1102 | - } |
1103 | - v, err = time.ParseDuration(enc) |
1104 | + return UnmarshalJSONViaString(ctd, b) |
1105 | +} |
1106 | + |
1107 | +func (ctd *ConfigTimeDuration) SetFromString(enc string) error { |
1108 | + v, err := time.ParseDuration(enc) |
1109 | if err != nil { |
1110 | return err |
1111 | } |
1112 | @@ -148,12 +164,11 @@ |
1113 | type ConfigHostPort string |
1114 | |
1115 | func (chp *ConfigHostPort) UnmarshalJSON(b []byte) error { |
1116 | - var enc string |
1117 | - err := json.Unmarshal(b, &enc) |
1118 | - if err != nil { |
1119 | - return err |
1120 | - } |
1121 | - _, _, err = net.SplitHostPort(enc) |
1122 | + return UnmarshalJSONViaString(chp, b) |
1123 | +} |
1124 | + |
1125 | +func (chp *ConfigHostPort) SetFromString(enc string) error { |
1126 | + _, _, err := net.SplitHostPort(enc) |
1127 | if err != nil { |
1128 | return err |
1129 | } |
1130 | @@ -198,23 +213,117 @@ |
1131 | return ioutil.ReadFile(p) |
1132 | } |
1133 | |
1134 | -// ReadFiles reads configuration from a set of files. Uses ReadConfig internally. |
1135 | +// used to implement getting config values with flag.Parse() |
1136 | +type val struct { |
1137 | + destField destField |
1138 | + accu map[string]json.RawMessage |
1139 | +} |
1140 | + |
1141 | +func (v *val) String() string { // used to show default |
1142 | + return string(v.accu[v.destField.configName()]) |
1143 | +} |
1144 | + |
1145 | +func (v *val) IsBoolFlag() bool { |
1146 | + return v.destField.fld.Type.Kind() == reflect.Bool |
1147 | +} |
1148 | + |
1149 | +func (v *val) marshalAsNeeded(s string) (json.RawMessage, error) { |
1150 | + var toMarshal interface{} |
1151 | + switch v.destField.dest.(type) { |
1152 | + case *string, FromString: |
1153 | + toMarshal = s |
1154 | + case *bool: |
1155 | + bit, err := strconv.ParseBool(s) |
1156 | + if err != nil { |
1157 | + return nil, err |
1158 | + } |
1159 | + toMarshal = bit |
1160 | + default: |
1161 | + return json.RawMessage(s), nil |
1162 | + } |
1163 | + return json.Marshal(toMarshal) |
1164 | +} |
1165 | + |
1166 | +func (v *val) Set(s string) error { |
1167 | + marshalled, err := v.marshalAsNeeded(s) |
1168 | + if err != nil { |
1169 | + return err |
1170 | + } |
1171 | + v.accu[v.destField.configName()] = marshalled |
1172 | + return nil |
1173 | +} |
1174 | + |
1175 | +func readOneConfig(accu map[string]json.RawMessage, cfgPath string) error { |
1176 | + r, err := os.Open(cfgPath) |
1177 | + if err != nil { |
1178 | + return err |
1179 | + } |
1180 | + defer r.Close() |
1181 | + err = json.NewDecoder(r).Decode(&accu) |
1182 | + if err != nil { |
1183 | + return err |
1184 | + } |
1185 | + return nil |
1186 | +} |
1187 | + |
1188 | +// used to implement -cfg@= |
1189 | +type readConfigAtVal struct { |
1190 | + accu map[string]json.RawMessage |
1191 | +} |
1192 | + |
1193 | +func (v *readConfigAtVal) String() string { |
1194 | + return "<config.json>" |
1195 | +} |
1196 | + |
1197 | +func (v *readConfigAtVal) Set(path string) error { |
1198 | + return readOneConfig(v.accu, path) |
1199 | +} |
1200 | + |
1201 | +// readUsingFlags gets config values from command line flags. |
1202 | +func readUsingFlags(accu map[string]json.RawMessage, destValue reflect.Value) error { |
1203 | + if flag.Parsed() { |
1204 | + if IgnoreParsedFlags { |
1205 | + return nil |
1206 | + } |
1207 | + return fmt.Errorf("too late, flags already parsed") |
1208 | + } |
1209 | + destStruct := destValue.Elem() |
1210 | + for destField := range traverseStruct(destStruct) { |
1211 | + help := destField.fld.Tag.Get("help") |
1212 | + flag.Var(&val{destField, accu}, destField.configName(), help) |
1213 | + } |
1214 | + flag.Var(&readConfigAtVal{accu}, "cfg@", "get config values from file") |
1215 | + flag.Parse() |
1216 | + return nil |
1217 | +} |
1218 | + |
1219 | +// IgnoreParsedFlags will just have ReadFiles ignore <flags> if the |
1220 | +// command line was already parsed. |
1221 | +var IgnoreParsedFlags = false |
1222 | + |
1223 | +// ReadFiles reads configuration from a set of files. The string |
1224 | +// "<flags>" can be used as a pseudo file-path, it will consider |
1225 | +// command line flags, invoking flag.Parse(). Among those the flag |
1226 | +// -cfg@=FILE can be used to get further config values from FILE. |
1227 | func ReadFiles(destConfig interface{}, cfgFpaths ...string) error { |
1228 | destValue, err := checkDestConfig("destConfig", destConfig) |
1229 | if err != nil { |
1230 | return err |
1231 | } |
1232 | // do the parsing in two phases for better error handling |
1233 | - var p1 map[string]json.RawMessage |
1234 | + p1 := make(map[string]json.RawMessage) |
1235 | readOne := false |
1236 | for _, cfgPath := range cfgFpaths { |
1237 | + if cfgPath == "<flags>" { |
1238 | + err := readUsingFlags(p1, destValue) |
1239 | + if err != nil { |
1240 | + return err |
1241 | + } |
1242 | + readOne = true |
1243 | + continue |
1244 | + } |
1245 | if _, err := os.Stat(cfgPath); err == nil { |
1246 | - r, err := os.Open(cfgPath) |
1247 | - if err != nil { |
1248 | - return err |
1249 | - } |
1250 | - defer r.Close() |
1251 | - err = json.NewDecoder(r).Decode(&p1) |
1252 | + err := readOneConfig(p1, cfgPath) |
1253 | if err != nil { |
1254 | return err |
1255 | } |
1256 | |
1257 | === modified file 'config/config_test.go' |
1258 | --- config/config_test.go 2014-03-25 18:49:18 +0000 |
1259 | +++ config/config_test.go 2014-04-29 18:02:00 +0000 |
1260 | @@ -18,6 +18,9 @@ |
1261 | |
1262 | import ( |
1263 | "bytes" |
1264 | + "encoding/json" |
1265 | + "flag" |
1266 | + "fmt" |
1267 | "io/ioutil" |
1268 | "os" |
1269 | "path/filepath" |
1270 | @@ -230,3 +233,105 @@ |
1271 | c.Check(res, DeepEquals, []string{"b", "c_list", "d"}) |
1272 | |
1273 | } |
1274 | + |
1275 | +type testConfig3 struct { |
1276 | + A bool |
1277 | + B string |
1278 | + C []string `json:"c_list"` |
1279 | + D ConfigTimeDuration `help:"duration"` |
1280 | + E ConfigHostPort |
1281 | + F string |
1282 | +} |
1283 | + |
1284 | +type configFlagsSuite struct{} |
1285 | + |
1286 | +var _ = Suite(&configFlagsSuite{}) |
1287 | + |
1288 | +func (s *configFlagsSuite) SetUpTest(c *C) { |
1289 | + flag.CommandLine = flag.NewFlagSet("cmd", flag.PanicOnError) |
1290 | + // supress outputs |
1291 | + flag.Usage = func() { flag.PrintDefaults() } |
1292 | + flag.CommandLine.SetOutput(ioutil.Discard) |
1293 | +} |
1294 | + |
1295 | +func (s *configFlagsSuite) TestReadUsingFlags(c *C) { |
1296 | + os.Args = []string{"cmd", "-a=1", "-b=foo", "-c_list", `["x","y"]`, "-d", "10s", "-e=localhost:80"} |
1297 | + var cfg testConfig3 |
1298 | + p := make(map[string]json.RawMessage) |
1299 | + err := readUsingFlags(p, reflect.ValueOf(&cfg)) |
1300 | + c.Assert(err, IsNil) |
1301 | + c.Check(p, DeepEquals, map[string]json.RawMessage{ |
1302 | + "a": json.RawMessage("true"), |
1303 | + "b": json.RawMessage(`"foo"`), |
1304 | + "c_list": json.RawMessage(`["x","y"]`), |
1305 | + "d": json.RawMessage(`"10s"`), |
1306 | + "e": json.RawMessage(`"localhost:80"`), |
1307 | + }) |
1308 | +} |
1309 | + |
1310 | +func (s *configFlagsSuite) TestReadUsingFlagsBoolError(c *C) { |
1311 | + os.Args = []string{"cmd", "-a=zoo"} |
1312 | + var cfg testConfig3 |
1313 | + p := make(map[string]json.RawMessage) |
1314 | + c.Check(func() { readUsingFlags(p, reflect.ValueOf(&cfg)) }, PanicMatches, ".*invalid boolean.*-a.*") |
1315 | +} |
1316 | + |
1317 | +func (s *configFlagsSuite) TestReadFilesAndFlags(c *C) { |
1318 | + // test <flags> pseudo file |
1319 | + os.Args = []string{"cmd", "-b=x"} |
1320 | + tmpDir := c.MkDir() |
1321 | + cfgPath := filepath.Join(tmpDir, "cfg.json") |
1322 | + err := ioutil.WriteFile(cfgPath, []byte(`{"a": 42, "c_list": ["y", "z"]}`), os.ModePerm) |
1323 | + c.Assert(err, IsNil) |
1324 | + var cfg testConfig1 |
1325 | + err = ReadFiles(&cfg, cfgPath, "<flags>") |
1326 | + c.Assert(err, IsNil) |
1327 | + c.Check(cfg.A, Equals, 42) |
1328 | + c.Check(cfg.B, Equals, "x") |
1329 | + c.Check(cfg.C, DeepEquals, []string{"y", "z"}) |
1330 | +} |
1331 | + |
1332 | +func (s *configFlagsSuite) TestReadFilesAndFlagsConfigAtSupport(c *C) { |
1333 | + // test <flags> pseudo file |
1334 | + tmpDir := c.MkDir() |
1335 | + cfgPath := filepath.Join(tmpDir, "cfg.json") |
1336 | + os.Args = []string{"cmd", "-a=42", fmt.Sprintf("-cfg@=%s", cfgPath)} |
1337 | + err := ioutil.WriteFile(cfgPath, []byte(`{"b": "x", "c_list": ["y", "z"]}`), os.ModePerm) |
1338 | + c.Assert(err, IsNil) |
1339 | + var cfg testConfig1 |
1340 | + err = ReadFiles(&cfg, "<flags>") |
1341 | + c.Assert(err, IsNil) |
1342 | + c.Check(cfg.A, Equals, 42) |
1343 | + c.Check(cfg.B, Equals, "x") |
1344 | + c.Check(cfg.C, DeepEquals, []string{"y", "z"}) |
1345 | +} |
1346 | + |
1347 | +func (s *configFlagsSuite) TestReadUsingFlagsHelp(c *C) { |
1348 | + os.Args = []string{"cmd", "-h"} |
1349 | + buf := bytes.NewBufferString("") |
1350 | + flag.CommandLine.Init("cmd", flag.ContinueOnError) |
1351 | + flag.CommandLine.SetOutput(buf) |
1352 | + var cfg testConfig3 |
1353 | + p := map[string]json.RawMessage{ |
1354 | + "d": json.RawMessage(`"2s"`), |
1355 | + } |
1356 | + readUsingFlags(p, reflect.ValueOf(&cfg)) |
1357 | + c.Check(buf.String(), Matches, `(?s).*-cfg@=<config.json>: get config values from file\n.*-d="2s": duration.*`) |
1358 | +} |
1359 | + |
1360 | +func (s *configFlagsSuite) TestReadUsingFlagsAlreadyParsed(c *C) { |
1361 | + os.Args = []string{"cmd"} |
1362 | + flag.Parse() |
1363 | + var cfg struct{} |
1364 | + p := make(map[string]json.RawMessage) |
1365 | + err := readUsingFlags(p, reflect.ValueOf(&cfg)) |
1366 | + c.Assert(err, ErrorMatches, "too late, flags already parsed") |
1367 | + err = ReadFiles(&cfg, "<flags>") |
1368 | + c.Assert(err, ErrorMatches, "too late, flags already parsed") |
1369 | + IgnoreParsedFlags = true |
1370 | + defer func() { |
1371 | + IgnoreParsedFlags = false |
1372 | + }() |
1373 | + err = ReadFiles(&cfg, "<flags>") |
1374 | + c.Assert(err, IsNil) |
1375 | +} |
1376 | |
1377 | === modified file 'debian/changelog' |
1378 | --- debian/changelog 2014-04-11 18:31:57 +0000 |
1379 | +++ debian/changelog 2014-04-29 18:02:00 +0000 |
1380 | @@ -1,3 +1,9 @@ |
1381 | +ubuntu-push (0.21-0.ubuntu1) UNRELEASED; urgency=medium |
1382 | + |
1383 | + * New upstream release: first auth bits, and Qt dependency. |
1384 | + |
1385 | + -- John Lenton <john.lenton@canonical.com> Tue, 15 Apr 2014 14:04:35 +0100 |
1386 | + |
1387 | ubuntu-push (0.2+14.04.20140411-0ubuntu1) trusty; urgency=medium |
1388 | |
1389 | [ John Lenton ] |
1390 | |
1391 | === modified file 'debian/control' |
1392 | --- debian/control 2014-03-25 16:26:20 +0000 |
1393 | +++ debian/control 2014-04-29 18:02:00 +0000 |
1394 | @@ -14,6 +14,10 @@ |
1395 | libgcrypt11-dev, |
1396 | libglib2.0-dev (>= 2.31.6), |
1397 | libwhoopsie-dev, |
1398 | + qtbase5-private-dev, |
1399 | + qtdeclarative5-dev, |
1400 | + libqt5opengl5-dev, |
1401 | + libubuntuoneauth-2.0-dev, |
1402 | Standards-Version: 3.9.5 |
1403 | Homepage: http://launchpad.net/ubuntu-push |
1404 | Vcs-Bzr: lp:ubuntu-push |
1405 | |
1406 | === modified file 'debian/rules' |
1407 | --- debian/rules 2014-03-24 12:22:55 +0000 |
1408 | +++ debian/rules 2014-04-29 18:02:00 +0000 |
1409 | @@ -2,9 +2,12 @@ |
1410 | # -*- makefile -*- |
1411 | |
1412 | export DH_GOPKG := launchpad.net/ubuntu-push |
1413 | -export DEB_BUILD_OPTIONS := nostrip |
1414 | export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR) |
1415 | |
1416 | +override_dh_auto_build: |
1417 | + cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1) && tar xvzf ../../externals.tgz |
1418 | + dh_auto_build --buildsystem=golang |
1419 | + |
1420 | override_dh_install: |
1421 | dh_install -Xusr/bin/cmd -Xusr/bin/dev --fail-missing |
1422 | |
1423 | |
1424 | === modified file 'dependencies.tsv' |
1425 | --- dependencies.tsv 2014-03-12 13:23:26 +0000 |
1426 | +++ dependencies.tsv 2014-04-29 18:02:00 +0000 |
1427 | @@ -2,3 +2,5 @@ |
1428 | launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140206110213-pbzcr6ucaz3rqmnw 125 |
1429 | launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10 |
1430 | launchpad.net/gocheck bzr gustavo@niemeyer.net-20140127131816-zshobk1qqme626xw 86 |
1431 | +gopkg.in/qml.v0 git master 8adbc8c2bf2da9f609df366683ad0f47a89c3d49 |
1432 | +gopkg.in/niemeyer/uoneauth.v1 git v1 0758ba882a143ad2862dbcac85a7ca145750b640 |
1433 | |
1434 | === added file 'externals.tgz' |
1435 | Binary files externals.tgz 1970-01-01 00:00:00 +0000 and externals.tgz 2014-04-29 18:02:00 +0000 differ |
1436 | === modified file 'logger/logger.go' |
1437 | --- logger/logger.go 2014-02-24 10:27:38 +0000 |
1438 | +++ logger/logger.go 2014-04-29 18:02:00 +0000 |
1439 | @@ -23,6 +23,8 @@ |
1440 | "log" |
1441 | "os" |
1442 | "runtime" |
1443 | + |
1444 | + "launchpad.net/ubuntu-push/config" |
1445 | ) |
1446 | |
1447 | // Logger is a simple logger interface with logging at levels. |
1448 | @@ -119,3 +121,28 @@ |
1449 | lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...)) |
1450 | } |
1451 | } |
1452 | + |
1453 | +// config bits |
1454 | + |
1455 | +// ConfigLogLevel can hold a log level in a configuration struct. |
1456 | +type ConfigLogLevel string |
1457 | + |
1458 | +func (cll *ConfigLogLevel) ConfigFromJSONString() {} |
1459 | + |
1460 | +func (cll *ConfigLogLevel) UnmarshalJSON(b []byte) error { |
1461 | + return config.UnmarshalJSONViaString(cll, b) |
1462 | +} |
1463 | + |
1464 | +func (cll *ConfigLogLevel) SetFromString(enc string) error { |
1465 | + _, ok := levelToNLevel[enc] |
1466 | + if !ok { |
1467 | + return fmt.Errorf("not a log level: %s", enc) |
1468 | + } |
1469 | + *cll = ConfigLogLevel(enc) |
1470 | + return nil |
1471 | +} |
1472 | + |
1473 | +// Level returns the log level string held in cll. |
1474 | +func (cll ConfigLogLevel) Level() string { |
1475 | + return string(cll) |
1476 | +} |
1477 | |
1478 | === modified file 'logger/logger_test.go' |
1479 | --- logger/logger_test.go 2014-02-10 22:51:43 +0000 |
1480 | +++ logger/logger_test.go 2014-04-29 18:02:00 +0000 |
1481 | @@ -25,6 +25,8 @@ |
1482 | "testing" |
1483 | |
1484 | . "launchpad.net/gocheck" |
1485 | + |
1486 | + "launchpad.net/ubuntu-push/config" |
1487 | ) |
1488 | |
1489 | func TestLogger(t *testing.T) { TestingT(t) } |
1490 | @@ -138,3 +140,26 @@ |
1491 | logger.Output(1, "foobaz") |
1492 | c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n") |
1493 | } |
1494 | + |
1495 | +type testLogLevelConfig struct { |
1496 | + Lvl ConfigLogLevel |
1497 | +} |
1498 | + |
1499 | +func (s *loggerSuite) TestReadConfigLogLevel(c *C) { |
1500 | + buf := bytes.NewBufferString(`{"lvl": "debug"}`) |
1501 | + var cfg testLogLevelConfig |
1502 | + err := config.ReadConfig(buf, &cfg) |
1503 | + c.Assert(err, IsNil) |
1504 | + c.Check(cfg.Lvl.Level(), Equals, "debug") |
1505 | +} |
1506 | + |
1507 | +func (s *loggerSuite) TestReadConfigLogLevelErrors(c *C) { |
1508 | + var cfg testLogLevelConfig |
1509 | + checkError := func(jsonCfg string, expectedError string) { |
1510 | + buf := bytes.NewBufferString(jsonCfg) |
1511 | + err := config.ReadConfig(buf, &cfg) |
1512 | + c.Check(err, ErrorMatches, expectedError) |
1513 | + } |
1514 | + checkError(`{"lvl": 1}`, "lvl:.*type string") |
1515 | + checkError(`{"lvl": "foo"}`, "lvl: not a log level: foo") |
1516 | +} |
1517 | |
1518 | === modified file 'protocol/messages.go' |
1519 | --- protocol/messages.go 2014-04-04 13:54:45 +0000 |
1520 | +++ protocol/messages.go 2014-04-29 18:02:00 +0000 |
1521 | @@ -54,6 +54,14 @@ |
1522 | Split() (done bool) |
1523 | } |
1524 | |
1525 | +// OnewayMsg are messages that are not to be followed by a response, |
1526 | +// after sending them the session either aborts or continues. |
1527 | +type OnewayMsg interface { |
1528 | + SplittableMsg |
1529 | + // continue session after the message? |
1530 | + OnewayContinue() bool |
1531 | +} |
1532 | + |
1533 | // CONNBROKEN message, server side is breaking the connection for reason. |
1534 | type ConnBrokenMsg struct { |
1535 | Type string `json:"T"` |
1536 | @@ -65,11 +73,35 @@ |
1537 | return true |
1538 | } |
1539 | |
1540 | +func (m *ConnBrokenMsg) OnewayContinue() bool { |
1541 | + return false |
1542 | +} |
1543 | + |
1544 | // CONNBROKEN reasons |
1545 | const ( |
1546 | BrokenHostMismatch = "host-mismatch" |
1547 | ) |
1548 | |
1549 | +// CONNWARN message, server side is warning about partial functionality |
1550 | +// because reason. |
1551 | +type ConnWarnMsg struct { |
1552 | + Type string `json:"T"` |
1553 | + // reason |
1554 | + Reason string |
1555 | +} |
1556 | + |
1557 | +func (m *ConnWarnMsg) Split() bool { |
1558 | + return true |
1559 | +} |
1560 | +func (m *ConnWarnMsg) OnewayContinue() bool { |
1561 | + return true |
1562 | +} |
1563 | + |
1564 | +// CONNWARN reasons |
1565 | +const ( |
1566 | + WarnUnauthorized = "unauthorized" |
1567 | +) |
1568 | + |
1569 | // PING/PONG messages |
1570 | type PingPongMsg struct { |
1571 | Type string `json:"T"` |
1572 | @@ -130,6 +162,19 @@ |
1573 | Payload json.RawMessage `json:"P"` |
1574 | } |
1575 | |
1576 | +// ExtractPayloads gets only the payloads out of a slice of notications. |
1577 | +func ExtractPayloads(notifications []Notification) []json.RawMessage { |
1578 | + n := len(notifications) |
1579 | + if n == 0 { |
1580 | + return nil |
1581 | + } |
1582 | + payloads := make([]json.RawMessage, n) |
1583 | + for i := 0; i < n; i++ { |
1584 | + payloads[i] = notifications[i].Payload |
1585 | + } |
1586 | + return payloads |
1587 | +} |
1588 | + |
1589 | // ACKnowledgement message |
1590 | type AckMsg struct { |
1591 | Type string `json:"T"` |
1592 | |
1593 | === modified file 'protocol/messages_test.go' |
1594 | --- protocol/messages_test.go 2014-04-04 13:19:10 +0000 |
1595 | +++ protocol/messages_test.go 2014-04-29 18:02:00 +0000 |
1596 | @@ -104,6 +104,22 @@ |
1597 | c.Check(b.splitting, Equals, 0) |
1598 | } |
1599 | |
1600 | -func (s *messagesSuite) TestSplitConnBrokenMsg(c *C) { |
1601 | - c.Check((&ConnBrokenMsg{}).Split(), Equals, true) |
1602 | +func (s *messagesSuite) TestConnBrokenMsg(c *C) { |
1603 | + m := &ConnBrokenMsg{} |
1604 | + c.Check(m.Split(), Equals, true) |
1605 | + c.Check(m.OnewayContinue(), Equals, false) |
1606 | +} |
1607 | + |
1608 | +func (s *messagesSuite) TestConnWarnMsg(c *C) { |
1609 | + m := &ConnWarnMsg{} |
1610 | + c.Check(m.Split(), Equals, true) |
1611 | + c.Check(m.OnewayContinue(), Equals, true) |
1612 | +} |
1613 | + |
1614 | +func (s *messagesSuite) TestExtractPayloads(c *C) { |
1615 | + c.Check(ExtractPayloads(nil), IsNil) |
1616 | + p1 := json.RawMessage(`{"a":1}`) |
1617 | + p2 := json.RawMessage(`{"b":2}`) |
1618 | + ns := []Notification{Notification{Payload: p1}, Notification{Payload: p2}} |
1619 | + c.Check(ExtractPayloads(ns), DeepEquals, []json.RawMessage{p1, p2}) |
1620 | } |
1621 | |
1622 | === modified file 'protocol/state-diag-client.gv' |
1623 | --- protocol/state-diag-client.gv 2014-01-16 20:07:13 +0000 |
1624 | +++ protocol/state-diag-client.gv 2014-04-29 18:02:00 +0000 |
1625 | @@ -2,7 +2,7 @@ |
1626 | label = "State diagram for client"; |
1627 | size="12,6"; |
1628 | rankdir=LR; |
1629 | - node [shape = doublecircle]; pingTimeout; |
1630 | + node [shape = doublecircle]; pingTimeout; connBroken; |
1631 | node [shape = circle]; |
1632 | start1 -> start2 [ label = "Write wire version" ]; |
1633 | start2 -> start3 [ label = "Write CONNECT" ]; |
1634 | @@ -13,4 +13,7 @@ |
1635 | broadcast -> loop [label = "Write ACK"]; |
1636 | loop -> pingTimeout [ |
1637 | label = "Elapsed ping interval + exchange interval"]; |
1638 | + loop -> connBroken [label = "Read CONNBROKEN"]; |
1639 | + loop -> warn [label = "Read CONNWARN"]; |
1640 | + warn -> loop; |
1641 | } |
1642 | |
1643 | === modified file 'protocol/state-diag-client.svg' |
1644 | --- protocol/state-diag-client.svg 2014-01-16 19:37:57 +0000 |
1645 | +++ protocol/state-diag-client.svg 2014-04-29 18:02:00 +0000 |
1646 | @@ -4,95 +4,123 @@ |
1647 | <!-- Generated by graphviz version 2.26.3 (20100126.1600) |
1648 | --> |
1649 | <!-- Title: state_diagram_client Pages: 1 --> |
1650 | -<svg width="864pt" height="279pt" |
1651 | - viewBox="0.00 0.00 864.00 278.89" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
1652 | -<g id="graph1" class="graph" transform="scale(0.683544 0.683544) rotate(0) translate(4 404)"> |
1653 | +<svg width="822pt" height="432pt" |
1654 | + viewBox="0.00 0.00 822.36 432.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
1655 | +<g id="graph1" class="graph" transform="scale(0.650602 0.650602) rotate(0) translate(4 660)"> |
1656 | <title>state_diagram_client</title> |
1657 | -<polygon fill="white" stroke="white" points="-4,5 -4,-404 1261,-404 1261,5 -4,5"/> |
1658 | +<polygon fill="white" stroke="white" points="-4,5 -4,-660 1261,-660 1261,5 -4,5"/> |
1659 | <text text-anchor="middle" x="628" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for client</text> |
1660 | <!-- pingTimeout --> |
1661 | <g id="node1" class="node"><title>pingTimeout</title> |
1662 | -<ellipse fill="none" stroke="black" cx="1180" cy="-324" rx="72.1249" ry="72.1249"/> |
1663 | -<ellipse fill="none" stroke="black" cx="1180" cy="-324" rx="76.1249" ry="76.1249"/> |
1664 | -<text text-anchor="middle" x="1180" y="-320.4" font-family="Times Roman,serif" font-size="14.00">pingTimeout</text> |
1665 | +<ellipse fill="none" stroke="black" cx="1180" cy="-580" rx="72.1249" ry="72.1249"/> |
1666 | +<ellipse fill="none" stroke="black" cx="1180" cy="-580" rx="76.1249" ry="76.1249"/> |
1667 | +<text text-anchor="middle" x="1180" y="-576.4" font-family="Times Roman,serif" font-size="14.00">pingTimeout</text> |
1668 | +</g> |
1669 | +<!-- connBroken --> |
1670 | +<g id="node2" class="node"><title>connBroken</title> |
1671 | +<ellipse fill="none" stroke="black" cx="1180" cy="-413" rx="68.8251" ry="69.2965"/> |
1672 | +<ellipse fill="none" stroke="black" cx="1180" cy="-413" rx="72.7978" ry="73.2965"/> |
1673 | +<text text-anchor="middle" x="1180" y="-409.4" font-family="Times Roman,serif" font-size="14.00">connBroken</text> |
1674 | </g> |
1675 | <!-- start1 --> |
1676 | -<g id="node2" class="node"><title>start1</title> |
1677 | -<ellipse fill="none" stroke="black" cx="42" cy="-166" rx="41.2167" ry="41.7193"/> |
1678 | -<text text-anchor="middle" x="42" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start1</text> |
1679 | +<g id="node3" class="node"><title>start1</title> |
1680 | +<ellipse fill="none" stroke="black" cx="42" cy="-231" rx="41.2167" ry="41.7193"/> |
1681 | +<text text-anchor="middle" x="42" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start1</text> |
1682 | </g> |
1683 | <!-- start2 --> |
1684 | -<g id="node4" class="node"><title>start2</title> |
1685 | -<ellipse fill="none" stroke="black" cx="292" cy="-166" rx="41.2167" ry="41.7193"/> |
1686 | -<text text-anchor="middle" x="292" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start2</text> |
1687 | +<g id="node5" class="node"><title>start2</title> |
1688 | +<ellipse fill="none" stroke="black" cx="292" cy="-231" rx="41.2167" ry="41.7193"/> |
1689 | +<text text-anchor="middle" x="292" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start2</text> |
1690 | </g> |
1691 | <!-- start1->start2 --> |
1692 | <g id="edge2" class="edge"><title>start1->start2</title> |
1693 | -<path fill="none" stroke="black" d="M83.5631,-166C126.547,-166 193.757,-166 240.181,-166"/> |
1694 | -<polygon fill="black" stroke="black" points="240.338,-169.5 250.338,-166 240.338,-162.5 240.338,-169.5"/> |
1695 | -<text text-anchor="middle" x="167" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Write wire version</text> |
1696 | +<path fill="none" stroke="black" d="M83.5631,-231C126.547,-231 193.757,-231 240.181,-231"/> |
1697 | +<polygon fill="black" stroke="black" points="240.338,-234.5 250.338,-231 240.338,-227.5 240.338,-234.5"/> |
1698 | +<text text-anchor="middle" x="167" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Write wire version</text> |
1699 | </g> |
1700 | <!-- start3 --> |
1701 | -<g id="node6" class="node"><title>start3</title> |
1702 | -<ellipse fill="none" stroke="black" cx="526" cy="-166" rx="41.2167" ry="41.7193"/> |
1703 | -<text text-anchor="middle" x="526" y="-162.4" font-family="Times Roman,serif" font-size="14.00">start3</text> |
1704 | +<g id="node7" class="node"><title>start3</title> |
1705 | +<ellipse fill="none" stroke="black" cx="526" cy="-231" rx="41.2167" ry="41.7193"/> |
1706 | +<text text-anchor="middle" x="526" y="-227.4" font-family="Times Roman,serif" font-size="14.00">start3</text> |
1707 | </g> |
1708 | <!-- start2->start3 --> |
1709 | <g id="edge4" class="edge"><title>start2->start3</title> |
1710 | -<path fill="none" stroke="black" d="M333.565,-166C372.875,-166 431.992,-166 474.321,-166"/> |
1711 | -<polygon fill="black" stroke="black" points="474.429,-169.5 484.429,-166 474.429,-162.5 474.429,-169.5"/> |
1712 | -<text text-anchor="middle" x="409" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Write CONNECT</text> |
1713 | +<path fill="none" stroke="black" d="M333.565,-231C372.875,-231 431.992,-231 474.321,-231"/> |
1714 | +<polygon fill="black" stroke="black" points="474.429,-234.5 484.429,-231 474.429,-227.5 474.429,-234.5"/> |
1715 | +<text text-anchor="middle" x="409" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Write CONNECT</text> |
1716 | </g> |
1717 | <!-- loop --> |
1718 | -<g id="node8" class="node"><title>loop</title> |
1719 | -<ellipse fill="none" stroke="black" cx="746" cy="-166" rx="31.8198" ry="31.8198"/> |
1720 | -<text text-anchor="middle" x="746" y="-162.4" font-family="Times Roman,serif" font-size="14.00">loop</text> |
1721 | +<g id="node9" class="node"><title>loop</title> |
1722 | +<ellipse fill="none" stroke="black" cx="746" cy="-231" rx="31.8198" ry="31.8198"/> |
1723 | +<text text-anchor="middle" x="746" y="-227.4" font-family="Times Roman,serif" font-size="14.00">loop</text> |
1724 | </g> |
1725 | <!-- start3->loop --> |
1726 | <g id="edge6" class="edge"><title>start3->loop</title> |
1727 | -<path fill="none" stroke="black" d="M567.639,-166C606.633,-166 664.616,-166 703.793,-166"/> |
1728 | -<polygon fill="black" stroke="black" points="703.818,-169.5 713.818,-166 703.818,-162.5 703.818,-169.5"/> |
1729 | -<text text-anchor="middle" x="641" y="-171.4" font-family="Times Roman,serif" font-size="14.00">Read CONNACK</text> |
1730 | +<path fill="none" stroke="black" d="M567.639,-231C606.633,-231 664.616,-231 703.793,-231"/> |
1731 | +<polygon fill="black" stroke="black" points="703.818,-234.5 713.818,-231 703.818,-227.5 703.818,-234.5"/> |
1732 | +<text text-anchor="middle" x="641" y="-236.4" font-family="Times Roman,serif" font-size="14.00">Read CONNACK</text> |
1733 | </g> |
1734 | <!-- loop->pingTimeout --> |
1735 | <g id="edge16" class="edge"><title>loop->pingTimeout</title> |
1736 | -<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"/> |
1737 | -<polygon fill="black" stroke="black" points="1093.67,-319.928 1104.02,-317.68 1094.53,-312.981 1093.67,-319.928"/> |
1738 | -<text text-anchor="middle" x="941" y="-319.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval + exchange interval</text> |
1739 | +<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"/> |
1740 | +<polygon fill="black" stroke="black" points="1093.96,-573.992 1104.39,-572.09 1095.05,-567.078 1093.96,-573.992"/> |
1741 | +<text text-anchor="middle" x="941" y="-572.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval + exchange interval</text> |
1742 | +</g> |
1743 | +<!-- loop->connBroken --> |
1744 | +<g id="edge18" class="edge"><title>loop->connBroken</title> |
1745 | +<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"/> |
1746 | +<polygon fill="black" stroke="black" points="1096.19,-416.445 1106.33,-413.388 1096.5,-409.452 1096.19,-416.445"/> |
1747 | +<text text-anchor="middle" x="941" y="-417.4" font-family="Times Roman,serif" font-size="14.00">Read CONNBROKEN</text> |
1748 | </g> |
1749 | <!-- pong --> |
1750 | -<g id="node10" class="node"><title>pong</title> |
1751 | -<ellipse fill="none" stroke="black" cx="1180" cy="-195" rx="34.8574" ry="35.3553"/> |
1752 | -<text text-anchor="middle" x="1180" y="-191.4" font-family="Times Roman,serif" font-size="14.00">pong</text> |
1753 | +<g id="node11" class="node"><title>pong</title> |
1754 | +<ellipse fill="none" stroke="black" cx="1180" cy="-287" rx="34.8574" ry="35.3553"/> |
1755 | +<text text-anchor="middle" x="1180" y="-283.4" font-family="Times Roman,serif" font-size="14.00">pong</text> |
1756 | </g> |
1757 | <!-- loop->pong --> |
1758 | <g id="edge8" class="edge"><title>loop->pong</title> |
1759 | -<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"/> |
1760 | -<polygon fill="black" stroke="black" points="1134.89,-202.186 1144.62,-198.003 1134.4,-195.203 1134.89,-202.186"/> |
1761 | -<text text-anchor="middle" x="941" y="-207.4" font-family="Times Roman,serif" font-size="14.00">Read PING</text> |
1762 | +<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"/> |
1763 | +<polygon fill="black" stroke="black" points="1135.49,-297.371 1144.94,-292.588 1134.57,-290.432 1135.49,-297.371"/> |
1764 | +<text text-anchor="middle" x="941" y="-307.4" font-family="Times Roman,serif" font-size="14.00">Read PING</text> |
1765 | </g> |
1766 | <!-- broadcast --> |
1767 | -<g id="node12" class="node"><title>broadcast</title> |
1768 | -<ellipse fill="none" stroke="black" cx="1180" cy="-84" rx="58.1882" ry="58.6899"/> |
1769 | -<text text-anchor="middle" x="1180" y="-80.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text> |
1770 | +<g id="node13" class="node"><title>broadcast</title> |
1771 | +<ellipse fill="none" stroke="black" cx="1180" cy="-176" rx="58.1882" ry="58.6899"/> |
1772 | +<text text-anchor="middle" x="1180" y="-172.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text> |
1773 | </g> |
1774 | <!-- loop->broadcast --> |
1775 | <g id="edge10" class="edge"><title>loop->broadcast</title> |
1776 | -<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"/> |
1777 | -<polygon fill="black" stroke="black" points="1113.34,-101.917 1122.5,-96.5998 1112.02,-95.0419 1113.34,-101.917"/> |
1778 | -<text text-anchor="middle" x="941" y="-136.4" font-family="Times Roman,serif" font-size="14.00">Read BROADCAST</text> |
1779 | +<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"/> |
1780 | +<polygon fill="black" stroke="black" points="1112.44,-189.353 1121.9,-184.574 1111.53,-182.413 1112.44,-189.353"/> |
1781 | +<text text-anchor="middle" x="941" y="-217.4" font-family="Times Roman,serif" font-size="14.00">Read BROADCAST</text> |
1782 | +</g> |
1783 | +<!-- warn --> |
1784 | +<g id="node19" class="node"><title>warn</title> |
1785 | +<ellipse fill="none" stroke="black" cx="1180" cy="-63" rx="36.7696" ry="36.7696"/> |
1786 | +<text text-anchor="middle" x="1180" y="-59.4" font-family="Times Roman,serif" font-size="14.00">warn</text> |
1787 | +</g> |
1788 | +<!-- loop->warn --> |
1789 | +<g id="edge20" class="edge"><title>loop->warn</title> |
1790 | +<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"/> |
1791 | +<polygon fill="black" stroke="black" points="1135.26,-79.0068 1144.04,-73.0757 1133.48,-72.2376 1135.26,-79.0068"/> |
1792 | +<text text-anchor="middle" x="941" y="-136.4" font-family="Times Roman,serif" font-size="14.00">Read CONNWARN</text> |
1793 | </g> |
1794 | <!-- pong->loop --> |
1795 | <g id="edge12" class="edge"><title>pong->loop</title> |
1796 | -<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"/> |
1797 | -<polygon fill="black" stroke="black" points="787.736,-157.528 778.16,-162.06 788.472,-164.489 787.736,-157.528"/> |
1798 | -<text text-anchor="middle" x="941" y="-168.4" font-family="Times Roman,serif" font-size="14.00">Write PONG</text> |
1799 | +<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"/> |
1800 | +<polygon fill="black" stroke="black" points="788.085,-227.996 778.035,-231.348 787.982,-234.995 788.085,-227.996"/> |
1801 | +<text text-anchor="middle" x="941" y="-254.4" font-family="Times Roman,serif" font-size="14.00">Write PONG</text> |
1802 | </g> |
1803 | <!-- broadcast->loop --> |
1804 | <g id="edge14" class="edge"><title>broadcast->loop</title> |
1805 | -<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"/> |
1806 | -<polygon fill="black" stroke="black" points="756.044,-124.528 755.336,-135.099 762.482,-127.277 756.044,-124.528"/> |
1807 | -<text text-anchor="middle" x="941" y="-86.4" font-family="Times Roman,serif" font-size="14.00">Write ACK</text> |
1808 | +<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"/> |
1809 | +<polygon fill="black" stroke="black" points="763.182,-192.043 760.465,-202.284 768.975,-195.973 763.182,-192.043"/> |
1810 | +<text text-anchor="middle" x="941" y="-172.4" font-family="Times Roman,serif" font-size="14.00">Write ACK</text> |
1811 | +</g> |
1812 | +<!-- warn->loop --> |
1813 | +<g id="edge22" class="edge"><title>warn->loop</title> |
1814 | +<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"/> |
1815 | +<polygon fill="black" stroke="black" points="751.574,-188.904 752.686,-199.44 758.387,-190.51 751.574,-188.904"/> |
1816 | </g> |
1817 | </g> |
1818 | </svg> |
1819 | |
1820 | === modified file 'protocol/state-diag-session.gv' |
1821 | --- protocol/state-diag-session.gv 2014-01-16 20:07:13 +0000 |
1822 | +++ protocol/state-diag-session.gv 2014-04-29 18:02:00 +0000 |
1823 | @@ -2,6 +2,7 @@ |
1824 | label = "State diagram for session"; |
1825 | size="12,6"; |
1826 | rankdir=LR; |
1827 | + node [shape = doublecircle]; stop; |
1828 | node [shape = circle]; |
1829 | start1 -> start2 [ label = "Read wire version" ]; |
1830 | start2 -> start3 [ label = "Read CONNECT" ]; |
1831 | @@ -17,4 +18,13 @@ |
1832 | split_broadcast -> split_ack_wait [label = "Write split BROADCAST"]; |
1833 | split_ack_wait -> split_broadcast [label = "Read ACK"]; |
1834 | split_broadcast -> loop [label = "All split msgs written"]; |
1835 | + // other |
1836 | + loop -> conn_broken [label = "Receive connbroken request"]; |
1837 | + loop -> conn_warn [label = "Receive connwarn request"]; |
1838 | + conn_broken -> stop [label = "Write CONNBROKEN"]; |
1839 | + conn_warn -> loop [label = "Write CONNWARN"]; |
1840 | + // timeouts |
1841 | + ack_wait -> stop [label = "Elapsed exhange timeout"]; |
1842 | + split_ack_wait -> stop [label = "Elapsed exhange timeout"]; |
1843 | + pong_wait -> stop [label = "Elapsed exhange timeout"]; |
1844 | } |
1845 | |
1846 | === modified file 'protocol/state-diag-session.svg' |
1847 | --- protocol/state-diag-session.svg 2014-01-16 19:37:57 +0000 |
1848 | +++ protocol/state-diag-session.svg 2014-04-29 18:02:00 +0000 |
1849 | @@ -4,139 +4,197 @@ |
1850 | <!-- Generated by graphviz version 2.26.3 (20100126.1600) |
1851 | --> |
1852 | <!-- Title: state_diagram_session Pages: 1 --> |
1853 | -<svg width="864pt" height="208pt" |
1854 | - viewBox="0.00 0.00 864.00 207.94" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
1855 | -<g id="graph1" class="graph" transform="scale(0.435923 0.435923) rotate(0) translate(4 473)"> |
1856 | +<svg width="864pt" height="266pt" |
1857 | + viewBox="0.00 0.00 864.00 265.73" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
1858 | +<g id="graph1" class="graph" transform="scale(0.367035 0.367035) rotate(0) translate(4 720)"> |
1859 | <title>state_diagram_session</title> |
1860 | -<polygon fill="white" stroke="white" points="-4,5 -4,-473 1979,-473 1979,5 -4,5"/> |
1861 | -<text text-anchor="middle" x="987" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for session</text> |
1862 | +<polygon fill="white" stroke="white" points="-4,5 -4,-720 2351,-720 2351,5 -4,5"/> |
1863 | +<text text-anchor="middle" x="1173" y="-9.4" font-family="Times Roman,serif" font-size="14.00">State diagram for session</text> |
1864 | +<!-- stop --> |
1865 | +<g id="node1" class="node"><title>stop</title> |
1866 | +<ellipse fill="none" stroke="black" cx="2309" cy="-335" rx="32.0813" ry="32.5269"/> |
1867 | +<ellipse fill="none" stroke="black" cx="2309" cy="-335" rx="36.0265" ry="36.5269"/> |
1868 | +<text text-anchor="middle" x="2309" y="-331.4" font-family="Times Roman,serif" font-size="14.00">stop</text> |
1869 | +</g> |
1870 | <!-- start1 --> |
1871 | -<g id="node1" class="node"><title>start1</title> |
1872 | -<ellipse fill="none" stroke="black" cx="42" cy="-294" rx="41.2167" ry="41.7193"/> |
1873 | -<text text-anchor="middle" x="42" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start1</text> |
1874 | +<g id="node2" class="node"><title>start1</title> |
1875 | +<ellipse fill="none" stroke="black" cx="42" cy="-395" rx="41.2167" ry="41.7193"/> |
1876 | +<text text-anchor="middle" x="42" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start1</text> |
1877 | </g> |
1878 | <!-- start2 --> |
1879 | -<g id="node3" class="node"><title>start2</title> |
1880 | -<ellipse fill="none" stroke="black" cx="286" cy="-294" rx="41.2167" ry="41.7193"/> |
1881 | -<text text-anchor="middle" x="286" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start2</text> |
1882 | +<g id="node4" class="node"><title>start2</title> |
1883 | +<ellipse fill="none" stroke="black" cx="286" cy="-395" rx="41.2167" ry="41.7193"/> |
1884 | +<text text-anchor="middle" x="286" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start2</text> |
1885 | </g> |
1886 | <!-- start1->start2 --> |
1887 | <g id="edge2" class="edge"><title>start1->start2</title> |
1888 | -<path fill="none" stroke="black" d="M83.6679,-294C125.213,-294 189.13,-294 233.981,-294"/> |
1889 | -<polygon fill="black" stroke="black" points="234.096,-297.5 244.096,-294 234.096,-290.5 234.096,-297.5"/> |
1890 | -<text text-anchor="middle" x="164" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Read wire version</text> |
1891 | +<path fill="none" stroke="black" d="M83.6679,-395C125.213,-395 189.13,-395 233.981,-395"/> |
1892 | +<polygon fill="black" stroke="black" points="234.096,-398.5 244.096,-395 234.096,-391.5 234.096,-398.5"/> |
1893 | +<text text-anchor="middle" x="164" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Read wire version</text> |
1894 | </g> |
1895 | <!-- start3 --> |
1896 | -<g id="node5" class="node"><title>start3</title> |
1897 | -<ellipse fill="none" stroke="black" cx="516" cy="-294" rx="41.2167" ry="41.7193"/> |
1898 | -<text text-anchor="middle" x="516" y="-290.4" font-family="Times Roman,serif" font-size="14.00">start3</text> |
1899 | +<g id="node6" class="node"><title>start3</title> |
1900 | +<ellipse fill="none" stroke="black" cx="537" cy="-395" rx="41.2167" ry="41.7193"/> |
1901 | +<text text-anchor="middle" x="537" y="-391.4" font-family="Times Roman,serif" font-size="14.00">start3</text> |
1902 | </g> |
1903 | <!-- start2->start3 --> |
1904 | <g id="edge4" class="edge"><title>start2->start3</title> |
1905 | -<path fill="none" stroke="black" d="M327.651,-294C365.959,-294 422.903,-294 464.145,-294"/> |
1906 | -<polygon fill="black" stroke="black" points="464.271,-297.5 474.271,-294 464.271,-290.5 464.271,-297.5"/> |
1907 | -<text text-anchor="middle" x="401" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Read CONNECT</text> |
1908 | +<path fill="none" stroke="black" d="M327.729,-395C370.886,-395 438.364,-395 484.973,-395"/> |
1909 | +<polygon fill="black" stroke="black" points="485.171,-398.5 495.171,-395 485.171,-391.5 485.171,-398.5"/> |
1910 | +<text text-anchor="middle" x="401" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Read CONNECT</text> |
1911 | </g> |
1912 | <!-- loop --> |
1913 | -<g id="node7" class="node"><title>loop</title> |
1914 | -<ellipse fill="none" stroke="black" cx="740" cy="-294" rx="31.8198" ry="31.8198"/> |
1915 | -<text text-anchor="middle" x="740" y="-290.4" font-family="Times Roman,serif" font-size="14.00">loop</text> |
1916 | +<g id="node8" class="node"><title>loop</title> |
1917 | +<ellipse fill="none" stroke="black" cx="790" cy="-395" rx="31.8198" ry="31.8198"/> |
1918 | +<text text-anchor="middle" x="790" y="-391.4" font-family="Times Roman,serif" font-size="14.00">loop</text> |
1919 | </g> |
1920 | <!-- start3->loop --> |
1921 | <g id="edge6" class="edge"><title>start3->loop</title> |
1922 | -<path fill="none" stroke="black" d="M557.608,-294C597.53,-294 657.517,-294 697.677,-294"/> |
1923 | -<polygon fill="black" stroke="black" points="697.687,-297.5 707.687,-294 697.687,-290.5 697.687,-297.5"/> |
1924 | -<text text-anchor="middle" x="633" y="-299.4" font-family="Times Roman,serif" font-size="14.00">Write CONNACK</text> |
1925 | +<path fill="none" stroke="black" d="M578.778,-395C625.49,-395 700.728,-395 747.665,-395"/> |
1926 | +<polygon fill="black" stroke="black" points="747.805,-398.5 757.805,-395 747.805,-391.5 747.805,-398.5"/> |
1927 | +<text text-anchor="middle" x="675" y="-400.4" font-family="Times Roman,serif" font-size="14.00">Write CONNACK</text> |
1928 | </g> |
1929 | <!-- ping --> |
1930 | -<g id="node9" class="node"><title>ping</title> |
1931 | -<ellipse fill="none" stroke="black" cx="1063" cy="-416" rx="32.0265" ry="32.5269"/> |
1932 | -<text text-anchor="middle" x="1063" y="-412.4" font-family="Times Roman,serif" font-size="14.00">ping</text> |
1933 | +<g id="node10" class="node"><title>ping</title> |
1934 | +<ellipse fill="none" stroke="black" cx="1135" cy="-593" rx="32.0265" ry="32.5269"/> |
1935 | +<text text-anchor="middle" x="1135" y="-589.4" font-family="Times Roman,serif" font-size="14.00">ping</text> |
1936 | </g> |
1937 | <!-- loop->ping --> |
1938 | <g id="edge8" class="edge"><title>loop->ping</title> |
1939 | -<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"/> |
1940 | -<polygon fill="black" stroke="black" points="1020.35,-420.33 1030.38,-416.906 1020.4,-413.33 1020.35,-420.33"/> |
1941 | -<text text-anchor="middle" x="881" y="-418.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval</text> |
1942 | +<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"/> |
1943 | +<polygon fill="black" stroke="black" points="1092.15,-591.877 1102.53,-589.734 1093.08,-584.939 1092.15,-591.877"/> |
1944 | +<text text-anchor="middle" x="946" y="-583.4" font-family="Times Roman,serif" font-size="14.00">Elapsed ping interval</text> |
1945 | </g> |
1946 | <!-- broadcast --> |
1947 | -<g id="node11" class="node"><title>broadcast</title> |
1948 | -<ellipse fill="none" stroke="black" cx="1063" cy="-200" rx="58.1882" ry="58.6899"/> |
1949 | -<text text-anchor="middle" x="1063" y="-196.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text> |
1950 | +<g id="node12" class="node"><title>broadcast</title> |
1951 | +<ellipse fill="none" stroke="black" cx="1135" cy="-281" rx="58.1882" ry="58.6899"/> |
1952 | +<text text-anchor="middle" x="1135" y="-277.4" font-family="Times Roman,serif" font-size="14.00">broadcast</text> |
1953 | </g> |
1954 | <!-- loop->broadcast --> |
1955 | <g id="edge10" class="edge"><title>loop->broadcast</title> |
1956 | -<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"/> |
1957 | -<polygon fill="black" stroke="black" points="995.396,-212.238 1004.75,-207.269 994.34,-205.318 995.396,-212.238"/> |
1958 | -<text text-anchor="middle" x="881" y="-267.4" font-family="Times Roman,serif" font-size="14.00">Receive broadcast request</text> |
1959 | +<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"/> |
1960 | +<polygon fill="black" stroke="black" points="1066.94,-289.319 1076.53,-284.811 1066.22,-282.355 1066.94,-289.319"/> |
1961 | +<text text-anchor="middle" x="946" y="-348.4" font-family="Times Roman,serif" font-size="14.00">Receive broadcast request</text> |
1962 | +</g> |
1963 | +<!-- conn_broken --> |
1964 | +<g id="node26" class="node"><title>conn_broken</title> |
1965 | +<ellipse fill="none" stroke="black" cx="1361" cy="-99" rx="73.0388" ry="73.5391"/> |
1966 | +<text text-anchor="middle" x="1361" y="-95.4" font-family="Times Roman,serif" font-size="14.00">conn_broken</text> |
1967 | +</g> |
1968 | +<!-- loop->conn_broken --> |
1969 | +<g id="edge28" class="edge"><title>loop->conn_broken</title> |
1970 | +<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"/> |
1971 | +<polygon fill="black" stroke="black" points="1278.34,-85.2954 1288.79,-83.4998 1279.5,-78.392 1278.34,-85.2954"/> |
1972 | +<text text-anchor="middle" x="946" y="-160.4" font-family="Times Roman,serif" font-size="14.00">Receive connbroken request</text> |
1973 | +</g> |
1974 | +<!-- conn_warn --> |
1975 | +<g id="node28" class="node"><title>conn_warn</title> |
1976 | +<ellipse fill="none" stroke="black" cx="1135" cy="-477" rx="65.7609" ry="65.7609"/> |
1977 | +<text text-anchor="middle" x="1135" y="-473.4" font-family="Times Roman,serif" font-size="14.00">conn_warn</text> |
1978 | +</g> |
1979 | +<!-- loop->conn_warn --> |
1980 | +<g id="edge30" class="edge"><title>loop->conn_warn</title> |
1981 | +<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"/> |
1982 | +<polygon fill="black" stroke="black" points="1058.71,-479.855 1068.85,-476.786 1059.01,-472.861 1058.71,-479.855"/> |
1983 | +<text text-anchor="middle" x="946" y="-480.4" font-family="Times Roman,serif" font-size="14.00">Receive connwarn request</text> |
1984 | </g> |
1985 | <!-- pong_wait --> |
1986 | -<g id="node13" class="node"><title>pong_wait</title> |
1987 | -<ellipse fill="none" stroke="black" cx="1526" cy="-406" rx="62.9325" ry="62.9325"/> |
1988 | -<text text-anchor="middle" x="1526" y="-402.4" font-family="Times Roman,serif" font-size="14.00">pong_wait</text> |
1989 | +<g id="node14" class="node"><title>pong_wait</title> |
1990 | +<ellipse fill="none" stroke="black" cx="537" cy="-653" rx="62.9325" ry="62.9325"/> |
1991 | +<text text-anchor="middle" x="537" y="-649.4" font-family="Times Roman,serif" font-size="14.00">pong_wait</text> |
1992 | </g> |
1993 | <!-- ping->pong_wait --> |
1994 | <g id="edge12" class="edge"><title>ping->pong_wait</title> |
1995 | -<path fill="none" stroke="black" d="M1095.56,-415.297C1169.19,-413.707 1350.04,-409.8 1452.36,-407.591"/> |
1996 | -<polygon fill="black" stroke="black" points="1452.69,-411.084 1462.61,-407.369 1452.54,-404.086 1452.69,-411.084"/> |
1997 | -<text text-anchor="middle" x="1289" y="-418.4" font-family="Times Roman,serif" font-size="14.00">Write PING</text> |
1998 | +<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"/> |
1999 | +<polygon fill="black" stroke="black" points="609.573,-662.637 599.214,-664.858 608.697,-669.582 609.573,-662.637"/> |
2000 | +<text text-anchor="middle" x="790" y="-670.4" font-family="Times Roman,serif" font-size="14.00">Write PING</text> |
2001 | </g> |
2002 | <!-- ack_wait --> |
2003 | -<g id="node15" class="node"><title>ack_wait</title> |
2004 | -<ellipse fill="none" stroke="black" cx="1526" cy="-269" rx="55.1543" ry="55.1543"/> |
2005 | -<text text-anchor="middle" x="1526" y="-265.4" font-family="Times Roman,serif" font-size="14.00">ack_wait</text> |
2006 | +<g id="node16" class="node"><title>ack_wait</title> |
2007 | +<ellipse fill="none" stroke="black" cx="1598" cy="-373" rx="55.1543" ry="55.1543"/> |
2008 | +<text text-anchor="middle" x="1598" y="-369.4" font-family="Times Roman,serif" font-size="14.00">ack_wait</text> |
2009 | </g> |
2010 | <!-- broadcast->ack_wait --> |
2011 | <g id="edge14" class="edge"><title>broadcast->ack_wait</title> |
2012 | -<path fill="none" stroke="black" d="M1121.17,-208.669C1207.93,-221.599 1370.7,-245.856 1461.17,-259.339"/> |
2013 | -<polygon fill="black" stroke="black" points="1460.9,-262.837 1471.3,-260.849 1461.93,-255.913 1460.9,-262.837"/> |
2014 | -<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> |
2015 | +<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"/> |
2016 | +<polygon fill="black" stroke="black" points="1534.79,-356.813 1545.37,-356.254 1536.75,-350.093 1534.79,-356.813"/> |
2017 | +<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> |
2018 | </g> |
2019 | <!-- split_broadcast --> |
2020 | -<g id="node17" class="node"><title>split_broadcast</title> |
2021 | -<ellipse fill="none" stroke="black" cx="1526" cy="-110" rx="84.1457" ry="84.1457"/> |
2022 | -<text text-anchor="middle" x="1526" y="-106.4" font-family="Times Roman,serif" font-size="14.00">split_broadcast</text> |
2023 | +<g id="node18" class="node"><title>split_broadcast</title> |
2024 | +<ellipse fill="none" stroke="black" cx="1598" cy="-216" rx="84.1457" ry="84.1457"/> |
2025 | +<text text-anchor="middle" x="1598" y="-212.4" font-family="Times Roman,serif" font-size="14.00">split_broadcast</text> |
2026 | </g> |
2027 | <!-- broadcast->split_broadcast --> |
2028 | <g id="edge16" class="edge"><title>broadcast->split_broadcast</title> |
2029 | -<path fill="none" stroke="black" d="M1120.7,-188.783C1199.06,-173.553 1340.01,-146.154 1433.29,-128.021"/> |
2030 | -<polygon fill="black" stroke="black" points="1434.15,-131.421 1443.29,-126.077 1432.81,-124.549 1434.15,-131.421"/> |
2031 | -<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> |
2032 | +<path fill="none" stroke="black" d="M1193.17,-272.834C1271.44,-261.846 1411.56,-242.174 1504.66,-229.104"/> |
2033 | +<polygon fill="black" stroke="black" points="1505.23,-232.558 1514.65,-227.702 1504.26,-225.626 1505.23,-232.558"/> |
2034 | +<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> |
2035 | +</g> |
2036 | +<!-- pong_wait->stop --> |
2037 | +<g id="edge40" class="edge"><title>pong_wait->stop</title> |
2038 | +<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"/> |
2039 | +<polygon fill="black" stroke="black" points="2292.5,-378.343 2293.84,-367.834 2286.24,-375.212 2292.5,-378.343"/> |
2040 | +<text text-anchor="middle" x="1361" y="-658.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text> |
2041 | </g> |
2042 | <!-- pong_wait->loop --> |
2043 | <g id="edge18" class="edge"><title>pong_wait->loop</title> |
2044 | -<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"/> |
2045 | -<polygon fill="black" stroke="black" points="781.898,-299.011 771.42,-300.582 780.59,-305.888 781.898,-299.011"/> |
2046 | -<text text-anchor="middle" x="1063" y="-359.4" font-family="Times Roman,serif" font-size="14.00">Read PONG</text> |
2047 | +<path fill="none" stroke="black" d="M581.359,-607.765C632.774,-555.333 716.085,-470.376 760.273,-425.314"/> |
2048 | +<polygon fill="black" stroke="black" points="762.774,-427.763 767.277,-418.172 757.776,-422.862 762.774,-427.763"/> |
2049 | +<text text-anchor="middle" x="675" y="-574.4" font-family="Times Roman,serif" font-size="14.00">Read PONG</text> |
2050 | +</g> |
2051 | +<!-- ack_wait->stop --> |
2052 | +<g id="edge36" class="edge"><title>ack_wait->stop</title> |
2053 | +<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"/> |
2054 | +<polygon fill="black" stroke="black" points="2263.58,-346.389 2272.99,-341.517 2262.59,-339.459 2263.58,-346.389"/> |
2055 | +<text text-anchor="middle" x="1972" y="-373.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text> |
2056 | </g> |
2057 | <!-- ack_wait->loop --> |
2058 | <g id="edge20" class="edge"><title>ack_wait->loop</title> |
2059 | -<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"/> |
2060 | -<polygon fill="black" stroke="black" points="781.977,-289.56 772.059,-293.288 782.136,-296.558 781.977,-289.56"/> |
2061 | -<text text-anchor="middle" x="1063" y="-292.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text> |
2062 | +<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"/> |
2063 | +<polygon fill="black" stroke="black" points="831.485,-386.79 821.871,-391.242 832.163,-393.757 831.485,-386.79"/> |
2064 | +<text text-anchor="middle" x="1135" y="-389.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text> |
2065 | </g> |
2066 | <!-- split_broadcast->loop --> |
2067 | <g id="edge26" class="edge"><title>split_broadcast->loop</title> |
2068 | -<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"/> |
2069 | -<polygon fill="black" stroke="black" points="753.014,-253.718 751.785,-264.241 759.308,-256.781 753.014,-253.718"/> |
2070 | -<text text-anchor="middle" x="1063" y="-120.4" font-family="Times Roman,serif" font-size="14.00">All split msgs written</text> |
2071 | +<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"/> |
2072 | +<polygon fill="black" stroke="black" points="800.027,-353.699 799.658,-364.287 806.549,-356.24 800.027,-353.699"/> |
2073 | +<text text-anchor="middle" x="1135" y="-201.4" font-family="Times Roman,serif" font-size="14.00">All split msgs written</text> |
2074 | </g> |
2075 | <!-- split_ack_wait --> |
2076 | -<g id="node21" class="node"><title>split_ack_wait</title> |
2077 | -<ellipse fill="none" stroke="black" cx="1893" cy="-110" rx="80.1095" ry="80.6102"/> |
2078 | -<text text-anchor="middle" x="1893" y="-106.4" font-family="Times Roman,serif" font-size="14.00">split_ack_wait</text> |
2079 | +<g id="node22" class="node"><title>split_ack_wait</title> |
2080 | +<ellipse fill="none" stroke="black" cx="1972" cy="-257" rx="80.1095" ry="80.6102"/> |
2081 | +<text text-anchor="middle" x="1972" y="-253.4" font-family="Times Roman,serif" font-size="14.00">split_ack_wait</text> |
2082 | </g> |
2083 | <!-- split_broadcast->split_ack_wait --> |
2084 | <g id="edge22" class="edge"><title>split_broadcast->split_ack_wait</title> |
2085 | -<path fill="none" stroke="black" d="M1610.2,-110C1667.61,-110 1743.59,-110 1802.33,-110"/> |
2086 | -<polygon fill="black" stroke="black" points="1802.35,-113.5 1812.35,-110 1802.34,-106.5 1802.35,-113.5"/> |
2087 | -<text text-anchor="middle" x="1711" y="-115.4" font-family="Times Roman,serif" font-size="14.00">Write split BROADCAST</text> |
2088 | +<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"/> |
2089 | +<polygon fill="black" stroke="black" points="1881.23,-255.523 1891.43,-252.676 1881.68,-248.537 1881.23,-255.523"/> |
2090 | +<text text-anchor="middle" x="1783" y="-256.4" font-family="Times Roman,serif" font-size="14.00">Write split BROADCAST</text> |
2091 | +</g> |
2092 | +<!-- split_ack_wait->stop --> |
2093 | +<g id="edge38" class="edge"><title>split_ack_wait->stop</title> |
2094 | +<path fill="none" stroke="black" d="M2050.59,-275.189C2116.55,-290.456 2208.51,-311.74 2263.08,-324.372"/> |
2095 | +<polygon fill="black" stroke="black" points="2262.62,-327.857 2273.15,-326.702 2264.2,-321.037 2262.62,-327.857"/> |
2096 | +<text text-anchor="middle" x="2166" y="-327.4" font-family="Times Roman,serif" font-size="14.00">Elapsed exhange timeout</text> |
2097 | </g> |
2098 | <!-- split_ack_wait->split_broadcast --> |
2099 | <g id="edge24" class="edge"><title>split_ack_wait->split_broadcast</title> |
2100 | -<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"/> |
2101 | -<polygon fill="black" stroke="black" points="1617.23,-85.8043 1607.87,-90.7602 1618.28,-92.7256 1617.23,-85.8043"/> |
2102 | -<text text-anchor="middle" x="1711" y="-93.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text> |
2103 | +<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"/> |
2104 | +<polygon fill="black" stroke="black" points="1691.25,-202.053 1681.52,-206.256 1691.74,-209.035 1691.25,-202.053"/> |
2105 | +<text text-anchor="middle" x="1783" y="-218.4" font-family="Times Roman,serif" font-size="14.00">Read ACK</text> |
2106 | +</g> |
2107 | +<!-- conn_broken->stop --> |
2108 | +<g id="edge32" class="edge"><title>conn_broken->stop</title> |
2109 | +<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"/> |
2110 | +<polygon fill="black" stroke="black" points="2275.42,-302.808 2284.91,-307.517 2280.43,-297.917 2275.42,-302.808"/> |
2111 | +<text text-anchor="middle" x="1783" y="-127.4" font-family="Times Roman,serif" font-size="14.00">Write CONNBROKEN</text> |
2112 | +</g> |
2113 | +<!-- conn_warn->loop --> |
2114 | +<g id="edge34" class="edge"><title>conn_warn->loop</title> |
2115 | +<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"/> |
2116 | +<polygon fill="black" stroke="black" points="831.758,-388.346 821.959,-392.373 832.131,-395.337 831.758,-388.346"/> |
2117 | +<text text-anchor="middle" x="946" y="-420.4" font-family="Times Roman,serif" font-size="14.00">Write CONNWARN</text> |
2118 | </g> |
2119 | </g> |
2120 | </svg> |
2121 | |
2122 | === modified file 'server/acceptance/acceptanceclient.go' |
2123 | --- server/acceptance/acceptanceclient.go 2014-04-09 19:30:53 +0000 |
2124 | +++ server/acceptance/acceptanceclient.go 2014-04-29 18:02:00 +0000 |
2125 | @@ -44,6 +44,7 @@ |
2126 | Levels map[string]int64 |
2127 | Insecure bool // don't verify certs |
2128 | Prefix string // prefix for events |
2129 | + Auth string |
2130 | // connection |
2131 | Connection net.Conn |
2132 | } |
2133 | @@ -73,6 +74,7 @@ |
2134 | Type string `json:"T"` |
2135 | protocol.BroadcastMsg |
2136 | protocol.NotificationsMsg |
2137 | + protocol.ConnWarnMsg |
2138 | } |
2139 | |
2140 | // Run the session with the server, emits a stream of events. |
2141 | @@ -93,6 +95,7 @@ |
2142 | "device": sess.Model, |
2143 | "channel": sess.ImageChannel, |
2144 | }, |
2145 | + Authorization: sess.Auth, |
2146 | }) |
2147 | if err != nil { |
2148 | return err |
2149 | @@ -136,6 +139,8 @@ |
2150 | return err |
2151 | } |
2152 | events <- fmt.Sprintf("%sbroadcast chan:%v app:%v topLevel:%d payloads:%s", sess.Prefix, recv.ChanId, recv.AppId, recv.TopLevel, pack) |
2153 | + case "warn": |
2154 | + events <- fmt.Sprintf("%swarn %s", sess.Prefix, recv.Reason) |
2155 | } |
2156 | } |
2157 | return nil |
2158 | |
2159 | === modified file 'server/acceptance/cmd/acceptanceclient.go' |
2160 | --- server/acceptance/cmd/acceptanceclient.go 2014-04-10 13:52:31 +0000 |
2161 | +++ server/acceptance/cmd/acceptanceclient.go 2014-04-29 18:02:00 +0000 |
2162 | @@ -48,13 +48,18 @@ |
2163 | fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <config.json> <device id>\n") |
2164 | flag.PrintDefaults() |
2165 | } |
2166 | + missingArg := func(what string) { |
2167 | + fmt.Fprintf(os.Stderr, "missing %s\n", what) |
2168 | + flag.Usage() |
2169 | + os.Exit(2) |
2170 | + } |
2171 | flag.Parse() |
2172 | narg := flag.NArg() |
2173 | switch { |
2174 | case narg < 1: |
2175 | - log.Fatal("missing config file") |
2176 | + missingArg("config file") |
2177 | case narg < 2: |
2178 | - log.Fatal("missing device-id") |
2179 | + missingArg("device-id") |
2180 | } |
2181 | configFName := flag.Arg(0) |
2182 | f, err := os.Open(configFName) |
2183 | |
2184 | === modified file 'server/acceptance/suites/broadcast.go' |
2185 | --- server/acceptance/suites/broadcast.go 2014-04-07 19:39:19 +0000 |
2186 | +++ server/acceptance/suites/broadcast.go 2014-04-29 18:02:00 +0000 |
2187 | @@ -265,7 +265,11 @@ |
2188 | |
2189 | func (s *BroadcastAcceptanceSuite) TestGetHosts(c *C) { |
2190 | gh := gethosts.New("", s.ServerAPIURL+"/delivery-hosts", 2*time.Second) |
2191 | - hosts, err := gh.Get() |
2192 | + host, err := gh.Get() |
2193 | c.Assert(err, IsNil) |
2194 | - c.Check(hosts, DeepEquals, []string{s.ServerAddr}) |
2195 | + expected := &gethosts.Host{ |
2196 | + Domain: "localhost", |
2197 | + Hosts: []string{s.ServerAddr}, |
2198 | + } |
2199 | + c.Check(host, DeepEquals, expected) |
2200 | } |
2201 | |
2202 | === modified file 'server/acceptance/suites/suite.go' |
2203 | --- server/acceptance/suites/suite.go 2014-04-03 16:47:47 +0000 |
2204 | +++ server/acceptance/suites/suite.go 2014-04-29 18:02:00 +0000 |
2205 | @@ -44,10 +44,19 @@ |
2206 | |
2207 | // Start a client. |
2208 | func (h *ServerHandle) StartClient(c *C, devId string, levels map[string]int64) (events <-chan string, errorCh <-chan error, stop func()) { |
2209 | + return h.StartClientAuth(c, devId, levels, "") |
2210 | +} |
2211 | + |
2212 | +// Start a client with auth. |
2213 | +func (h *ServerHandle) StartClientAuth(c *C, devId string, levels map[string]int64, auth string) (events <-chan string, errorCh <-chan error, stop func()) { |
2214 | errCh := make(chan error, 1) |
2215 | cliEvents := make(chan string, 10) |
2216 | sess := testClientSession(h.ServerAddr, devId, "m1", "img1", false) |
2217 | sess.Levels = levels |
2218 | + sess.Auth = auth |
2219 | + if auth != "" { |
2220 | + sess.ExchangeTimeout = 5 * time.Second |
2221 | + } |
2222 | err := sess.Dial() |
2223 | c.Assert(err, IsNil) |
2224 | clientShutdown := make(chan bool, 1) // abused as an atomic flag |
2225 | |
2226 | === modified file 'server/api/handlers_test.go' |
2227 | --- server/api/handlers_test.go 2014-02-21 11:32:38 +0000 |
2228 | +++ server/api/handlers_test.go 2014-04-29 18:02:00 +0000 |
2229 | @@ -30,6 +30,7 @@ |
2230 | |
2231 | . "launchpad.net/gocheck" |
2232 | |
2233 | + "launchpad.net/ubuntu-push/protocol" |
2234 | "launchpad.net/ubuntu-push/server/store" |
2235 | helpers "launchpad.net/ubuntu-push/testing" |
2236 | ) |
2237 | @@ -142,11 +143,11 @@ |
2238 | } |
2239 | |
2240 | func (cbsend *checkBrokerSending) Broadcast(chanId store.InternalChannelId) { |
2241 | - top, payloads, err := cbsend.store.GetChannelSnapshot(chanId) |
2242 | + top, notifications, err := cbsend.store.GetChannelSnapshot(chanId) |
2243 | cbsend.err = err |
2244 | cbsend.chanId = chanId |
2245 | cbsend.top = top |
2246 | - cbsend.payloads = payloads |
2247 | + cbsend.payloads = protocol.ExtractPayloads(notifications) |
2248 | } |
2249 | |
2250 | func (s *handlersSuite) TestDoBroadcast(c *C) { |
2251 | |
2252 | === modified file 'server/broker/broker.go' |
2253 | --- server/broker/broker.go 2014-04-04 09:58:34 +0000 |
2254 | +++ server/broker/broker.go 2014-04-29 18:02:00 +0000 |
2255 | @@ -30,7 +30,7 @@ |
2256 | // through them. |
2257 | type Broker interface { |
2258 | // Register the session. |
2259 | - Register(*protocol.ConnectMsg) (BrokerSession, error) |
2260 | + Register(connMsg *protocol.ConnectMsg, sessionId string) (BrokerSession, error) |
2261 | // Unregister the session. |
2262 | Unregister(BrokerSession) |
2263 | } |
2264 | |
2265 | === modified file 'server/broker/exchanges.go' |
2266 | --- server/broker/exchanges.go 2014-04-04 13:19:10 +0000 |
2267 | +++ server/broker/exchanges.go 2014-04-29 18:02:00 +0000 |
2268 | @@ -28,18 +28,17 @@ |
2269 | |
2270 | // Scratch area for exchanges, sessions should hold one of these. |
2271 | type ExchangesScratchArea struct { |
2272 | - broadcastMsg protocol.BroadcastMsg |
2273 | - ackMsg protocol.AckMsg |
2274 | - connBrokenMsg protocol.ConnBrokenMsg |
2275 | + broadcastMsg protocol.BroadcastMsg |
2276 | + ackMsg protocol.AckMsg |
2277 | } |
2278 | |
2279 | // BroadcastExchange leads a session through delivering a BROADCAST. |
2280 | // For simplicity it is fully public. |
2281 | type BroadcastExchange struct { |
2282 | - ChanId store.InternalChannelId |
2283 | - TopLevel int64 |
2284 | - NotificationPayloads []json.RawMessage |
2285 | - Decoded []map[string]interface{} |
2286 | + ChanId store.InternalChannelId |
2287 | + TopLevel int64 |
2288 | + Notifications []protocol.Notification |
2289 | + Decoded []map[string]interface{} |
2290 | } |
2291 | |
2292 | // check interface already here |
2293 | @@ -47,18 +46,18 @@ |
2294 | |
2295 | // Init ensures the BroadcastExchange is fully initialized for the sessions. |
2296 | func (sbe *BroadcastExchange) Init() { |
2297 | - decoded := make([]map[string]interface{}, len(sbe.NotificationPayloads)) |
2298 | + decoded := make([]map[string]interface{}, len(sbe.Notifications)) |
2299 | sbe.Decoded = decoded |
2300 | - for i, p := range sbe.NotificationPayloads { |
2301 | - err := json.Unmarshal(p, &decoded[i]) |
2302 | + for i, notif := range sbe.Notifications { |
2303 | + err := json.Unmarshal(notif.Payload, &decoded[i]) |
2304 | if err != nil { |
2305 | decoded[i] = nil |
2306 | } |
2307 | } |
2308 | } |
2309 | |
2310 | -func filterByLevel(clientLevel, topLevel int64, payloads []json.RawMessage) []json.RawMessage { |
2311 | - c := int64(len(payloads)) |
2312 | +func filterByLevel(clientLevel, topLevel int64, notifs []protocol.Notification) []protocol.Notification { |
2313 | + c := int64(len(notifs)) |
2314 | if c == 0 { |
2315 | return nil |
2316 | } |
2317 | @@ -67,32 +66,32 @@ |
2318 | delta = 1 |
2319 | } |
2320 | if delta < c { |
2321 | - return payloads[c-delta:] |
2322 | + return notifs[c-delta:] |
2323 | } else { |
2324 | - return payloads |
2325 | + return notifs |
2326 | } |
2327 | } |
2328 | |
2329 | -func channelFilter(tag string, chanId store.InternalChannelId, payloads []json.RawMessage, decoded []map[string]interface{}) []json.RawMessage { |
2330 | - if len(payloads) != 0 && chanId == store.SystemInternalChannelId { |
2331 | - decoded := decoded[len(decoded)-len(payloads):] |
2332 | +func channelFilter(tag string, chanId store.InternalChannelId, notifs []protocol.Notification, decoded []map[string]interface{}) []json.RawMessage { |
2333 | + if len(notifs) != 0 && chanId == store.SystemInternalChannelId { |
2334 | + decoded := decoded[len(decoded)-len(notifs):] |
2335 | filtered := make([]json.RawMessage, 0) |
2336 | for i, decoded1 := range decoded { |
2337 | if _, ok := decoded1[tag]; ok { |
2338 | - filtered = append(filtered, payloads[i]) |
2339 | + filtered = append(filtered, notifs[i].Payload) |
2340 | } |
2341 | } |
2342 | - payloads = filtered |
2343 | + return filtered |
2344 | } |
2345 | - return payloads |
2346 | + return protocol.ExtractPayloads(notifs) |
2347 | } |
2348 | |
2349 | // Prepare session for a BROADCAST. |
2350 | func (sbe *BroadcastExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) { |
2351 | clientLevel := sess.Levels()[sbe.ChanId] |
2352 | - payloads := filterByLevel(clientLevel, sbe.TopLevel, sbe.NotificationPayloads) |
2353 | + notifs := filterByLevel(clientLevel, sbe.TopLevel, sbe.Notifications) |
2354 | tag := fmt.Sprintf("%s/%s", sess.DeviceImageChannel(), sess.DeviceImageModel()) |
2355 | - payloads = channelFilter(tag, sbe.ChanId, payloads, sbe.Decoded) |
2356 | + payloads := channelFilter(tag, sbe.ChanId, notifs, sbe.Decoded) |
2357 | if len(payloads) == 0 && sbe.TopLevel >= clientLevel { |
2358 | // empty and don't need to force resync => do nothing |
2359 | return nil, nil, ErrNop |
2360 | @@ -119,23 +118,20 @@ |
2361 | return nil |
2362 | } |
2363 | |
2364 | -// ConnBrokenExchange breaks a session giving a reason. |
2365 | -type ConnBrokenExchange struct { |
2366 | - Reason string |
2367 | +// ConnMetaExchange allows to send a CONNBROKEN or CONNWARN message. |
2368 | +type ConnMetaExchange struct { |
2369 | + Msg protocol.OnewayMsg |
2370 | } |
2371 | |
2372 | // check interface already here |
2373 | -var _ Exchange = (*ConnBrokenExchange)(nil) |
2374 | +var _ Exchange = (*ConnMetaExchange)(nil) |
2375 | |
2376 | -// Prepare session for a CONNBROKEN. |
2377 | -func (cbe *ConnBrokenExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) { |
2378 | - scratchArea := sess.ExchangeScratchArea() |
2379 | - scratchArea.connBrokenMsg.Type = "connbroken" |
2380 | - scratchArea.connBrokenMsg.Reason = cbe.Reason |
2381 | - return &scratchArea.connBrokenMsg, nil, nil |
2382 | +// Prepare session for a CONNBROKEN/WARN. |
2383 | +func (cbe *ConnMetaExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) { |
2384 | + return cbe.Msg, nil, nil |
2385 | } |
2386 | |
2387 | -// CONNBROKEN isn't acked |
2388 | -func (cbe *ConnBrokenExchange) Acked(sess BrokerSession, done bool) error { |
2389 | - panic("Acked should not get invoked on ConnBrokenExchange") |
2390 | +// CONNBROKEN/WARN aren't acked. |
2391 | +func (cbe *ConnMetaExchange) Acked(sess BrokerSession, done bool) error { |
2392 | + panic("Acked should not get invoked on ConnMetaExchange") |
2393 | } |
2394 | |
2395 | === modified file 'server/broker/exchanges_test.go' |
2396 | --- server/broker/exchanges_test.go 2014-04-04 13:19:10 +0000 |
2397 | +++ server/broker/exchanges_test.go 2014-04-29 18:02:00 +0000 |
2398 | @@ -24,9 +24,11 @@ |
2399 | |
2400 | . "launchpad.net/gocheck" |
2401 | |
2402 | + "launchpad.net/ubuntu-push/protocol" |
2403 | "launchpad.net/ubuntu-push/server/broker" |
2404 | "launchpad.net/ubuntu-push/server/broker/testing" |
2405 | "launchpad.net/ubuntu-push/server/store" |
2406 | + help "launchpad.net/ubuntu-push/testing" |
2407 | ) |
2408 | |
2409 | func TestBroker(t *stdtesting.T) { TestingT(t) } |
2410 | @@ -39,11 +41,11 @@ |
2411 | exchg := &broker.BroadcastExchange{ |
2412 | ChanId: store.SystemInternalChannelId, |
2413 | TopLevel: 3, |
2414 | - NotificationPayloads: []json.RawMessage{ |
2415 | + Notifications: help.Ns( |
2416 | json.RawMessage(`{"a":"x"}`), |
2417 | json.RawMessage(`[]`), |
2418 | json.RawMessage(`{"a":"y"}`), |
2419 | - }, |
2420 | + ), |
2421 | } |
2422 | exchg.Init() |
2423 | c.Check(exchg.Decoded, DeepEquals, []map[string]interface{}{ |
2424 | @@ -62,10 +64,10 @@ |
2425 | exchg := &broker.BroadcastExchange{ |
2426 | ChanId: store.SystemInternalChannelId, |
2427 | TopLevel: 3, |
2428 | - NotificationPayloads: []json.RawMessage{ |
2429 | + Notifications: help.Ns( |
2430 | json.RawMessage(`{"img1/m1":100}`), |
2431 | json.RawMessage(`{"img2/m2":200}`), |
2432 | - }, |
2433 | + ), |
2434 | } |
2435 | exchg.Init() |
2436 | outMsg, inMsg, err := exchg.Prepare(sess) |
2437 | @@ -88,9 +90,9 @@ |
2438 | ImageChannel: "img1", |
2439 | } |
2440 | exchg := &broker.BroadcastExchange{ |
2441 | - ChanId: store.SystemInternalChannelId, |
2442 | - TopLevel: 3, |
2443 | - NotificationPayloads: []json.RawMessage{}, |
2444 | + ChanId: store.SystemInternalChannelId, |
2445 | + TopLevel: 3, |
2446 | + Notifications: []protocol.Notification{}, |
2447 | } |
2448 | exchg.Init() |
2449 | outMsg, inMsg, err := exchg.Prepare(sess) |
2450 | @@ -108,9 +110,9 @@ |
2451 | ImageChannel: "img1", |
2452 | } |
2453 | exchg := &broker.BroadcastExchange{ |
2454 | - ChanId: store.SystemInternalChannelId, |
2455 | - TopLevel: 3, |
2456 | - NotificationPayloads: []json.RawMessage{}, |
2457 | + ChanId: store.SystemInternalChannelId, |
2458 | + TopLevel: 3, |
2459 | + Notifications: []protocol.Notification{}, |
2460 | } |
2461 | exchg.Init() |
2462 | outMsg, inMsg, err := exchg.Prepare(sess) |
2463 | @@ -133,9 +135,9 @@ |
2464 | |
2465 | topLevel := int64(len(needsSplitting)) |
2466 | exchg := &broker.BroadcastExchange{ |
2467 | - ChanId: store.SystemInternalChannelId, |
2468 | - TopLevel: topLevel, |
2469 | - NotificationPayloads: needsSplitting, |
2470 | + ChanId: store.SystemInternalChannelId, |
2471 | + TopLevel: topLevel, |
2472 | + Notifications: help.Ns(needsSplitting...), |
2473 | } |
2474 | exchg.Init() |
2475 | outMsg, _, err := exchg.Prepare(sess) |
2476 | @@ -152,10 +154,10 @@ |
2477 | exchg = &broker.BroadcastExchange{ |
2478 | ChanId: store.SystemInternalChannelId, |
2479 | TopLevel: topLevel + 2, |
2480 | - NotificationPayloads: []json.RawMessage{ |
2481 | + Notifications: help.Ns( |
2482 | json.RawMessage(`{"img1/m1":"x"}`), |
2483 | json.RawMessage(`{"img1/m1":"y"}`), |
2484 | - }, |
2485 | + ), |
2486 | } |
2487 | exchg.Init() |
2488 | outMsg, _, err = exchg.Prepare(sess) |
2489 | @@ -173,9 +175,9 @@ |
2490 | exchg := &broker.BroadcastExchange{ |
2491 | ChanId: store.SystemInternalChannelId, |
2492 | TopLevel: 3, |
2493 | - NotificationPayloads: []json.RawMessage{ |
2494 | + Notifications: help.Ns( |
2495 | json.RawMessage(`{"img2/m1":1}`), |
2496 | - }, |
2497 | + ), |
2498 | } |
2499 | exchg.Init() |
2500 | outMsg, inMsg, err := exchg.Prepare(sess) |
2501 | @@ -202,10 +204,10 @@ |
2502 | exchg := &broker.BroadcastExchange{ |
2503 | ChanId: store.SystemInternalChannelId, |
2504 | TopLevel: 3, |
2505 | - NotificationPayloads: []json.RawMessage{ |
2506 | + Notifications: help.Ns( |
2507 | json.RawMessage(`{"img1/m1":100}`), |
2508 | json.RawMessage(`{"img1/m1":101}`), |
2509 | - }, |
2510 | + ), |
2511 | } |
2512 | exchg.Init() |
2513 | outMsg, inMsg, err := exchg.Prepare(sess) |
2514 | @@ -229,11 +231,11 @@ |
2515 | exchg := &broker.BroadcastExchange{ |
2516 | ChanId: store.SystemInternalChannelId, |
2517 | TopLevel: 5, |
2518 | - NotificationPayloads: []json.RawMessage{ |
2519 | + Notifications: help.Ns( |
2520 | json.RawMessage(`{"img1/m1":100}`), |
2521 | json.RawMessage(`{"img2/m2":200}`), |
2522 | json.RawMessage(`{"img1/m1":101}`), |
2523 | - }, |
2524 | + ), |
2525 | } |
2526 | exchg.Init() |
2527 | outMsg, inMsg, err := exchg.Prepare(sess) |
2528 | @@ -249,16 +251,18 @@ |
2529 | c.Check(sess.LevelsMap[store.SystemInternalChannelId], Equals, int64(5)) |
2530 | } |
2531 | |
2532 | -func (s *exchangesSuite) TestConnBrokenExchange(c *C) { |
2533 | +func (s *exchangesSuite) TestConnMetaExchange(c *C) { |
2534 | sess := &testing.TestBrokerSession{} |
2535 | - cbe := &broker.ConnBrokenExchange{"REASON"} |
2536 | + var msg protocol.OnewayMsg = &protocol.ConnWarnMsg{"connwarn", "REASON"} |
2537 | + cbe := &broker.ConnMetaExchange{msg} |
2538 | outMsg, inMsg, err := cbe.Prepare(sess) |
2539 | c.Assert(err, IsNil) |
2540 | + c.Check(msg, Equals, outMsg) |
2541 | c.Check(inMsg, IsNil) // no answer is expected |
2542 | // check |
2543 | marshalled, err := json.Marshal(outMsg) |
2544 | c.Assert(err, IsNil) |
2545 | - c.Check(string(marshalled), Equals, `{"T":"connbroken","Reason":"REASON"}`) |
2546 | + c.Check(string(marshalled), Equals, `{"T":"connwarn","Reason":"REASON"}`) |
2547 | |
2548 | - c.Check(func() { cbe.Acked(nil, true) }, PanicMatches, "Acked should not get invoked on ConnBrokenExchange") |
2549 | + c.Check(func() { cbe.Acked(nil, true) }, PanicMatches, "Acked should not get invoked on ConnMetaExchange") |
2550 | } |
2551 | |
2552 | === modified file 'server/broker/exchg_impl_test.go' |
2553 | --- server/broker/exchg_impl_test.go 2014-04-03 16:00:53 +0000 |
2554 | +++ server/broker/exchg_impl_test.go 2014-04-29 18:02:00 +0000 |
2555 | @@ -22,6 +22,7 @@ |
2556 | . "launchpad.net/gocheck" |
2557 | |
2558 | "launchpad.net/ubuntu-push/server/store" |
2559 | + help "launchpad.net/ubuntu-push/testing" |
2560 | ) |
2561 | |
2562 | type exchangesImplSuite struct{} |
2563 | @@ -29,27 +30,27 @@ |
2564 | var _ = Suite(&exchangesImplSuite{}) |
2565 | |
2566 | func (s *exchangesImplSuite) TestFilterByLevel(c *C) { |
2567 | - payloads := []json.RawMessage{ |
2568 | + notifs := help.Ns( |
2569 | json.RawMessage(`{"a": 3}`), |
2570 | json.RawMessage(`{"a": 4}`), |
2571 | json.RawMessage(`{"a": 5}`), |
2572 | - } |
2573 | - res := filterByLevel(5, 5, payloads) |
2574 | + ) |
2575 | + res := filterByLevel(5, 5, notifs) |
2576 | c.Check(len(res), Equals, 0) |
2577 | - res = filterByLevel(4, 5, payloads) |
2578 | + res = filterByLevel(4, 5, notifs) |
2579 | c.Check(len(res), Equals, 1) |
2580 | - c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 5}`)) |
2581 | - res = filterByLevel(3, 5, payloads) |
2582 | + c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 5}`)) |
2583 | + res = filterByLevel(3, 5, notifs) |
2584 | c.Check(len(res), Equals, 2) |
2585 | - c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 4}`)) |
2586 | - res = filterByLevel(2, 5, payloads) |
2587 | + c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 4}`)) |
2588 | + res = filterByLevel(2, 5, notifs) |
2589 | c.Check(len(res), Equals, 3) |
2590 | - res = filterByLevel(1, 5, payloads) |
2591 | + res = filterByLevel(1, 5, notifs) |
2592 | c.Check(len(res), Equals, 3) |
2593 | // too ahead, pick only last |
2594 | - res = filterByLevel(10, 5, payloads) |
2595 | + res = filterByLevel(10, 5, notifs) |
2596 | c.Check(len(res), Equals, 1) |
2597 | - c.Check(res[0], DeepEquals, json.RawMessage(`{"a": 5}`)) |
2598 | + c.Check(res[0].Payload, DeepEquals, json.RawMessage(`{"a": 5}`)) |
2599 | } |
2600 | |
2601 | func (s *exchangesImplSuite) TestFilterByLevelEmpty(c *C) { |
2602 | @@ -71,18 +72,19 @@ |
2603 | err := json.Unmarshal(p, &decoded[i]) |
2604 | c.Assert(err, IsNil) |
2605 | } |
2606 | + notifs := help.Ns(payloads...) |
2607 | |
2608 | other := store.InternalChannelId("1") |
2609 | |
2610 | c.Check(channelFilter("", store.SystemInternalChannelId, nil, nil), IsNil) |
2611 | - c.Check(channelFilter("", other, payloads[1:], decoded), DeepEquals, payloads[1:]) |
2612 | + c.Check(channelFilter("", other, notifs[1:], decoded), DeepEquals, payloads[1:]) |
2613 | |
2614 | // use tag when channel is the sytem channel |
2615 | |
2616 | - c.Check(channelFilter("c/z", store.SystemInternalChannelId, payloads, decoded), HasLen, 0) |
2617 | - |
2618 | - c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads, decoded), DeepEquals, []json.RawMessage{payloads[0], payloads[3]}) |
2619 | - |
2620 | - c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads[1:], decoded), DeepEquals, []json.RawMessage{payloads[3]}) |
2621 | + c.Check(channelFilter("c/z", store.SystemInternalChannelId, notifs, decoded), HasLen, 0) |
2622 | + |
2623 | + c.Check(channelFilter("a/x", store.SystemInternalChannelId, notifs, decoded), DeepEquals, []json.RawMessage{payloads[0], payloads[3]}) |
2624 | + |
2625 | + c.Check(channelFilter("a/x", store.SystemInternalChannelId, notifs[1:], decoded), DeepEquals, []json.RawMessage{payloads[3]}) |
2626 | |
2627 | } |
2628 | |
2629 | === modified file 'server/broker/simple/simple.go' |
2630 | --- server/broker/simple/simple.go 2014-04-11 08:47:18 +0000 |
2631 | +++ server/broker/simple/simple.go 2014-04-29 18:02:00 +0000 |
2632 | @@ -144,7 +144,7 @@ |
2633 | // find relevant channels, for now only system |
2634 | channels := []store.InternalChannelId{store.SystemInternalChannelId} |
2635 | for _, chanId := range channels { |
2636 | - topLevel, payloads, err := b.sto.GetChannelSnapshot(chanId) |
2637 | + topLevel, notifications, err := b.sto.GetChannelSnapshot(chanId) |
2638 | if err != nil { |
2639 | // next broadcast will try again |
2640 | b.logger.Errorf("unsuccessful feed pending, get channel snapshot for %v: %v", chanId, err) |
2641 | @@ -153,9 +153,9 @@ |
2642 | clientLevel := sess.levels[chanId] |
2643 | if clientLevel != topLevel { |
2644 | broadcastExchg := &broker.BroadcastExchange{ |
2645 | - ChanId: chanId, |
2646 | - TopLevel: topLevel, |
2647 | - NotificationPayloads: payloads, |
2648 | + ChanId: chanId, |
2649 | + TopLevel: topLevel, |
2650 | + Notifications: notifications, |
2651 | } |
2652 | broadcastExchg.Init() |
2653 | sess.exchanges <- broadcastExchg |
2654 | @@ -166,7 +166,7 @@ |
2655 | |
2656 | // Register registers a session with the broker. It feeds the session |
2657 | // pending notifications as well. |
2658 | -func (b *SimpleBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) { |
2659 | +func (b *SimpleBroker) Register(connect *protocol.ConnectMsg, sessionId string) (broker.BrokerSession, error) { |
2660 | // xxx sanity check DeviceId |
2661 | model, err := broker.GetInfoString(connect, "device", "?") |
2662 | if err != nil { |
2663 | @@ -224,7 +224,7 @@ |
2664 | } else { // register |
2665 | prev := b.registry[sess.deviceId] |
2666 | if prev != nil { // kick it |
2667 | - close(prev.exchanges) |
2668 | + prev.exchanges <- nil |
2669 | } |
2670 | b.registry[sess.deviceId] = sess |
2671 | sess.registered = true |
2672 | @@ -233,16 +233,16 @@ |
2673 | case delivery := <-b.deliveryCh: |
2674 | switch delivery.kind { |
2675 | case broadcastDelivery: |
2676 | - topLevel, payloads, err := b.sto.GetChannelSnapshot(delivery.chanId) |
2677 | + topLevel, notifications, err := b.sto.GetChannelSnapshot(delivery.chanId) |
2678 | if err != nil { |
2679 | // next broadcast will try again |
2680 | b.logger.Errorf("unsuccessful broadcast, get channel snapshot for %v: %v", delivery.chanId, err) |
2681 | continue Loop |
2682 | } |
2683 | broadcastExchg := &broker.BroadcastExchange{ |
2684 | - ChanId: delivery.chanId, |
2685 | - TopLevel: topLevel, |
2686 | - NotificationPayloads: payloads, |
2687 | + ChanId: delivery.chanId, |
2688 | + TopLevel: topLevel, |
2689 | + Notifications: notifications, |
2690 | } |
2691 | broadcastExchg.Init() |
2692 | for _, sess := range b.registry { |
2693 | |
2694 | === modified file 'server/broker/simple/simple_test.go' |
2695 | --- server/broker/simple/simple_test.go 2014-04-03 16:00:53 +0000 |
2696 | +++ server/broker/simple/simple_test.go 2014-04-29 18:02:00 +0000 |
2697 | @@ -26,6 +26,7 @@ |
2698 | "launchpad.net/ubuntu-push/server/broker" |
2699 | "launchpad.net/ubuntu-push/server/broker/testing" |
2700 | "launchpad.net/ubuntu-push/server/store" |
2701 | + help "launchpad.net/ubuntu-push/testing" |
2702 | ) |
2703 | |
2704 | func TestSimple(t *stdtesting.T) { TestingT(t) } |
2705 | @@ -58,10 +59,10 @@ |
2706 | c.Assert(len(sess.exchanges), Equals, 1) |
2707 | exchg1 := <-sess.exchanges |
2708 | c.Check(exchg1, DeepEquals, &broker.BroadcastExchange{ |
2709 | - ChanId: store.SystemInternalChannelId, |
2710 | - TopLevel: 1, |
2711 | - NotificationPayloads: []json.RawMessage{notification1}, |
2712 | - Decoded: []map[string]interface{}{decoded1}, |
2713 | + ChanId: store.SystemInternalChannelId, |
2714 | + TopLevel: 1, |
2715 | + Notifications: help.Ns(notification1), |
2716 | + Decoded: []map[string]interface{}{decoded1}, |
2717 | }) |
2718 | } |
2719 | |
2720 | |
2721 | === modified file 'server/broker/testsuite/suite.go' |
2722 | --- server/broker/testsuite/suite.go 2014-04-11 08:47:18 +0000 |
2723 | +++ server/broker/testsuite/suite.go 2014-04-29 18:02:00 +0000 |
2724 | @@ -30,7 +30,7 @@ |
2725 | "launchpad.net/ubuntu-push/server/broker" |
2726 | "launchpad.net/ubuntu-push/server/broker/testing" |
2727 | "launchpad.net/ubuntu-push/server/store" |
2728 | - helpers "launchpad.net/ubuntu-push/testing" |
2729 | + help "launchpad.net/ubuntu-push/testing" |
2730 | ) |
2731 | |
2732 | // The expected interface for tested brokers. |
2733 | @@ -51,11 +51,11 @@ |
2734 | // Let us get to a broker.BroadcastExchange from an Exchange. |
2735 | RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange |
2736 | // private |
2737 | - testlog *helpers.TestLogger |
2738 | + testlog *help.TestLogger |
2739 | } |
2740 | |
2741 | func (s *CommonBrokerSuite) SetUpTest(c *C) { |
2742 | - s.testlog = helpers.NewTestLogger(c, "error") |
2743 | + s.testlog = help.NewTestLogger(c, "error") |
2744 | } |
2745 | |
2746 | var testBrokerConfig = &testing.TestBrokerConfig{10, 5} |
2747 | @@ -89,7 +89,7 @@ |
2748 | "device": "model", |
2749 | "channel": "daily", |
2750 | }, |
2751 | - }) |
2752 | + }, "s1") |
2753 | c.Assert(err, IsNil) |
2754 | c.Assert(s.RevealSession(b, "dev-1"), Equals, sess) |
2755 | c.Assert(sess.DeviceIdentifier(), Equals, "dev-1") |
2756 | @@ -101,7 +101,7 @@ |
2757 | })) |
2758 | b.Unregister(sess) |
2759 | // just to make sure the unregister was processed |
2760 | - _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}) |
2761 | + _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}, "s2") |
2762 | c.Assert(err, IsNil) |
2763 | c.Check(s.RevealSession(b, "dev-1"), IsNil) |
2764 | } |
2765 | @@ -111,7 +111,7 @@ |
2766 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
2767 | b.Start() |
2768 | defer b.Stop() |
2769 | - _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}) |
2770 | + _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}, "s1") |
2771 | c.Check(err, FitsTypeOf, &broker.ErrAbort{}) |
2772 | } |
2773 | |
2774 | @@ -123,11 +123,11 @@ |
2775 | info := map[string]interface{}{ |
2776 | "device": -1, |
2777 | } |
2778 | - _, err := b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}) |
2779 | + _, err := b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}, "s1") |
2780 | c.Check(err, Equals, broker.ErrUnexpectedValue) |
2781 | info["device"] = "m" |
2782 | info["channel"] = -1 |
2783 | - _, err = b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}) |
2784 | + _, err = b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}, "s2") |
2785 | c.Check(err, Equals, broker.ErrUnexpectedValue) |
2786 | } |
2787 | |
2788 | @@ -139,7 +139,7 @@ |
2789 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
2790 | b.Start() |
2791 | defer b.Stop() |
2792 | - sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2793 | + sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1") |
2794 | c.Assert(err, IsNil) |
2795 | c.Check(len(sess.SessionChannel()), Equals, 1) |
2796 | } |
2797 | @@ -149,7 +149,7 @@ |
2798 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
2799 | b.Start() |
2800 | defer b.Stop() |
2801 | - _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2802 | + _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1") |
2803 | c.Assert(err, IsNil) |
2804 | // but |
2805 | c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n") |
2806 | @@ -160,22 +160,25 @@ |
2807 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
2808 | b.Start() |
2809 | defer b.Stop() |
2810 | - sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2811 | - c.Assert(err, IsNil) |
2812 | - sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2813 | - c.Assert(err, IsNil) |
2814 | - checkAndFalse := false |
2815 | - // previous session got signaled by closing its channel |
2816 | + sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1") |
2817 | + c.Assert(err, IsNil) |
2818 | + sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s2") |
2819 | + c.Assert(err, IsNil) |
2820 | + // previous session got signaled by sending nil on its channel |
2821 | + var sentinel broker.Exchange |
2822 | + got := false |
2823 | select { |
2824 | - case _, ok := <-sess1.SessionChannel(): |
2825 | - checkAndFalse = ok == false |
2826 | - default: |
2827 | + case sentinel = <-sess1.SessionChannel(): |
2828 | + got = true |
2829 | + case <-time.After(5 * time.Second): |
2830 | + c.Fatal("taking too long to get sentinel") |
2831 | } |
2832 | - c.Check(checkAndFalse, Equals, true) |
2833 | + c.Check(got, Equals, true) |
2834 | + c.Check(sentinel, IsNil) |
2835 | c.Assert(s.RevealSession(b, "dev-1"), Equals, sess2) |
2836 | b.Unregister(sess1) |
2837 | // just to make sure the unregister was processed |
2838 | - _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}) |
2839 | + _, err = b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: ""}, "s3") |
2840 | c.Assert(err, IsNil) |
2841 | c.Check(s.RevealSession(b, "dev-1"), Equals, sess2) |
2842 | } |
2843 | @@ -187,9 +190,9 @@ |
2844 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
2845 | b.Start() |
2846 | defer b.Stop() |
2847 | - sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2848 | + sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1") |
2849 | c.Assert(err, IsNil) |
2850 | - sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-2"}) |
2851 | + sess2, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-2"}, "s2") |
2852 | c.Assert(err, IsNil) |
2853 | // add notification to channel *after* the registrations |
2854 | muchLater := time.Now().Add(10 * time.Minute) |
2855 | @@ -200,10 +203,10 @@ |
2856 | c.Fatal("taking too long to get broadcast exchange") |
2857 | case exchg1 := <-sess1.SessionChannel(): |
2858 | c.Check(s.RevealBroadcastExchange(exchg1), DeepEquals, &broker.BroadcastExchange{ |
2859 | - ChanId: store.SystemInternalChannelId, |
2860 | - TopLevel: 1, |
2861 | - NotificationPayloads: []json.RawMessage{notification1}, |
2862 | - Decoded: []map[string]interface{}{decoded1}, |
2863 | + ChanId: store.SystemInternalChannelId, |
2864 | + TopLevel: 1, |
2865 | + Notifications: help.Ns(notification1), |
2866 | + Decoded: []map[string]interface{}{decoded1}, |
2867 | }) |
2868 | } |
2869 | select { |
2870 | @@ -211,10 +214,10 @@ |
2871 | c.Fatal("taking too long to get broadcast exchange") |
2872 | case exchg2 := <-sess2.SessionChannel(): |
2873 | c.Check(s.RevealBroadcastExchange(exchg2), DeepEquals, &broker.BroadcastExchange{ |
2874 | - ChanId: store.SystemInternalChannelId, |
2875 | - TopLevel: 1, |
2876 | - NotificationPayloads: []json.RawMessage{notification1}, |
2877 | - Decoded: []map[string]interface{}{decoded1}, |
2878 | + ChanId: store.SystemInternalChannelId, |
2879 | + TopLevel: 1, |
2880 | + Notifications: help.Ns(notification1), |
2881 | + Decoded: []map[string]interface{}{decoded1}, |
2882 | }) |
2883 | } |
2884 | } |
2885 | @@ -224,7 +227,7 @@ |
2886 | countdownToFail int |
2887 | } |
2888 | |
2889 | -func (sto *testFailingStore) GetChannelSnapshot(chanId store.InternalChannelId) (int64, []json.RawMessage, error) { |
2890 | +func (sto *testFailingStore) GetChannelSnapshot(chanId store.InternalChannelId) (int64, []protocol.Notification, error) { |
2891 | if sto.countdownToFail == 0 { |
2892 | return 0, nil, errors.New("get channel snapshot fail") |
2893 | } |
2894 | @@ -241,7 +244,7 @@ |
2895 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
2896 | b.Start() |
2897 | defer b.Stop() |
2898 | - _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}) |
2899 | + _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, "s1") |
2900 | c.Assert(err, IsNil) |
2901 | b.Broadcast(store.SystemInternalChannelId) |
2902 | select { |
2903 | |
2904 | === modified file 'server/session/session.go' |
2905 | --- server/session/session.go 2014-04-11 08:47:18 +0000 |
2906 | +++ server/session/session.go 2014-04-29 18:02:00 +0000 |
2907 | @@ -18,6 +18,7 @@ |
2908 | package session |
2909 | |
2910 | import ( |
2911 | + "errors" |
2912 | "net" |
2913 | "time" |
2914 | |
2915 | @@ -35,7 +36,7 @@ |
2916 | } |
2917 | |
2918 | // sessionStart manages the start of the protocol session. |
2919 | -func sessionStart(proto protocol.Protocol, brkr broker.Broker, cfg SessionConfig) (broker.BrokerSession, error) { |
2920 | +func sessionStart(proto protocol.Protocol, brkr broker.Broker, cfg SessionConfig, sessionId string) (broker.BrokerSession, error) { |
2921 | var connMsg protocol.ConnectMsg |
2922 | proto.SetDeadline(time.Now().Add(cfg.ExchangeTimeout())) |
2923 | err := proto.ReadMessage(&connMsg) |
2924 | @@ -52,9 +53,11 @@ |
2925 | if err != nil { |
2926 | return nil, err |
2927 | } |
2928 | - return brkr.Register(&connMsg) |
2929 | + return brkr.Register(&connMsg, sessionId) |
2930 | } |
2931 | |
2932 | +var errOneway = errors.New("oneway") |
2933 | + |
2934 | // exchange writes outMsg message, reads answer in inMsg |
2935 | func exchange(proto protocol.Protocol, outMsg, inMsg interface{}, exchangeTimeout time.Duration) error { |
2936 | proto.SetDeadline(time.Now().Add(exchangeTimeout)) |
2937 | @@ -62,7 +65,10 @@ |
2938 | if err != nil { |
2939 | return err |
2940 | } |
2941 | - if inMsg == nil { // no answer expected, breaking connection |
2942 | + if inMsg == nil { // no answer expected |
2943 | + if outMsg.(protocol.OnewayMsg).OnewayContinue() { |
2944 | + return errOneway |
2945 | + } |
2946 | return &broker.ErrAbort{"session broken for reason"} |
2947 | } |
2948 | err = proto.ReadMessage(inMsg) |
2949 | @@ -78,6 +84,10 @@ |
2950 | exchangeTimeout := cfg.ExchangeTimeout() |
2951 | pingTimer := time.NewTimer(pingInterval) |
2952 | intervalStart := time.Now() |
2953 | + pingTimerReset := func() { |
2954 | + pingTimer.Reset(pingInterval) |
2955 | + intervalStart = time.Now() |
2956 | + } |
2957 | ch := sess.SessionChannel() |
2958 | Loop: |
2959 | for { |
2960 | @@ -93,16 +103,15 @@ |
2961 | if pongMsg.Type != "pong" { |
2962 | return &broker.ErrAbort{"expected PONG message"} |
2963 | } |
2964 | - pingTimer.Reset(pingInterval) |
2965 | - case exchg, ok := <-ch: |
2966 | + pingTimerReset() |
2967 | + case exchg := <-ch: |
2968 | pingTimer.Stop() |
2969 | - if !ok { |
2970 | + if exchg == nil { |
2971 | return &broker.ErrAbort{"terminated"} |
2972 | } |
2973 | outMsg, inMsg, err := exchg.Prepare(sess) |
2974 | if err == broker.ErrNop { // nothing to do |
2975 | - pingTimer.Reset(pingInterval) |
2976 | - intervalStart = time.Now() |
2977 | + pingTimerReset() |
2978 | continue Loop |
2979 | } |
2980 | if err != nil { |
2981 | @@ -111,12 +120,15 @@ |
2982 | for { |
2983 | done := outMsg.Split() |
2984 | err = exchange(proto, outMsg, inMsg, exchangeTimeout) |
2985 | + if err == errOneway { |
2986 | + pingTimerReset() |
2987 | + continue Loop |
2988 | + } |
2989 | if err != nil { |
2990 | return err |
2991 | } |
2992 | if done { |
2993 | - pingTimer.Reset(pingInterval) |
2994 | - intervalStart = time.Now() |
2995 | + pingTimerReset() |
2996 | } |
2997 | err = exchg.Acked(sess, done) |
2998 | if err != nil { |
2999 | @@ -142,7 +154,7 @@ |
3000 | return track.End(&broker.ErrAbort{"unexpected wire format version"}) |
3001 | } |
3002 | proto := protocol.NewProtocol0(conn) |
3003 | - sess, err := sessionStart(proto, brkr, cfg) |
3004 | + sess, err := sessionStart(proto, brkr, cfg, track.SessionId()) |
3005 | if err != nil { |
3006 | return track.End(err) |
3007 | } |
3008 | |
3009 | === modified file 'server/session/session_test.go' |
3010 | --- server/session/session_test.go 2014-04-11 08:47:18 +0000 |
3011 | +++ server/session/session_test.go 2014-04-29 18:02:00 +0000 |
3012 | @@ -130,8 +130,8 @@ |
3013 | return &testBroker{registration: make(chan interface{}, 2)} |
3014 | } |
3015 | |
3016 | -func (tb *testBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) { |
3017 | - tb.registration <- "register " + connect.DeviceId |
3018 | +func (tb *testBroker) Register(connect *protocol.ConnectMsg, sessionId string) (broker.BrokerSession, error) { |
3019 | + tb.registration <- fmt.Sprintf("register %s %s", connect.DeviceId, sessionId) |
3020 | return &testing.TestBrokerSession{DeviceId: connect.DeviceId}, tb.err |
3021 | } |
3022 | |
3023 | @@ -148,7 +148,7 @@ |
3024 | brkr := newTestBroker() |
3025 | go func() { |
3026 | var err error |
3027 | - sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout) |
3028 | + sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout, "s1") |
3029 | errCh <- err |
3030 | }() |
3031 | c.Check(takeNext(down), Equals, "deadline 5ms") |
3032 | @@ -160,7 +160,7 @@ |
3033 | up <- nil // no write error |
3034 | err := <-errCh |
3035 | c.Check(err, IsNil) |
3036 | - c.Check(takeNext(brkr.registration), Equals, "register dev-1") |
3037 | + c.Check(takeNext(brkr.registration), Equals, "register dev-1 s1") |
3038 | c.Check(sess.DeviceIdentifier(), Equals, "dev-1") |
3039 | } |
3040 | |
3041 | @@ -175,7 +175,7 @@ |
3042 | brkr.err = errRegister |
3043 | go func() { |
3044 | var err error |
3045 | - sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout) |
3046 | + sess, err = sessionStart(tp, brkr, cfg10msPingInterval5msExchangeTout, "s2") |
3047 | errCh <- err |
3048 | }() |
3049 | up <- protocol.ConnectMsg{Type: "connect", ClientVer: "1", DeviceId: "dev-1"} |
3050 | @@ -190,7 +190,7 @@ |
3051 | down := make(chan interface{}, 5) |
3052 | tp := &testProtocol{up, down} |
3053 | up <- io.ErrUnexpectedEOF |
3054 | - _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout) |
3055 | + _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s3") |
3056 | c.Check(err, Equals, io.ErrUnexpectedEOF) |
3057 | } |
3058 | |
3059 | @@ -200,7 +200,7 @@ |
3060 | tp := &testProtocol{up, down} |
3061 | up <- protocol.ConnectMsg{Type: "connect"} |
3062 | up <- io.ErrUnexpectedEOF |
3063 | - _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout) |
3064 | + _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s4") |
3065 | c.Check(err, Equals, io.ErrUnexpectedEOF) |
3066 | // sanity |
3067 | c.Check(takeNext(down), Matches, "deadline.*") |
3068 | @@ -212,7 +212,7 @@ |
3069 | down := make(chan interface{}, 5) |
3070 | tp := &testProtocol{up, down} |
3071 | up <- protocol.ConnectMsg{Type: "what"} |
3072 | - _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout) |
3073 | + _, err := sessionStart(tp, nil, cfg10msPingInterval5msExchangeTout, "s5") |
3074 | c.Check(err, DeepEquals, &broker.ErrAbort{"expected CONNECT message"}) |
3075 | } |
3076 | |
3077 | @@ -222,14 +222,14 @@ |
3078 | } |
3079 | |
3080 | func (s *sessionSuite) TestSessionLoop(c *C) { |
3081 | - nopTrack := NewTracker(s.testlog) |
3082 | + track := &testTracker{NewTracker(s.testlog), make(chan interface{}, 2)} |
3083 | errCh := make(chan error, 1) |
3084 | up := make(chan interface{}, 5) |
3085 | down := make(chan interface{}, 5) |
3086 | tp := &testProtocol{up, down} |
3087 | sess := &testing.TestBrokerSession{} |
3088 | go func() { |
3089 | - errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack) |
3090 | + errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, track) |
3091 | }() |
3092 | c.Check(takeNext(down), Equals, "deadline 2ms") |
3093 | c.Check(takeNext(down), DeepEquals, protocol.PingPongMsg{Type: "ping"}) |
3094 | @@ -241,6 +241,9 @@ |
3095 | up <- io.ErrUnexpectedEOF |
3096 | err := <-errCh |
3097 | c.Check(err, Equals, io.ErrUnexpectedEOF) |
3098 | + c.Check(track.interval, HasLen, 2) |
3099 | + c.Check((<-track.interval).(time.Duration) <= 8*time.Millisecond, Equals, true) |
3100 | + c.Check((<-track.interval).(time.Duration) <= 8*time.Millisecond, Equals, true) |
3101 | } |
3102 | |
3103 | func (s *sessionSuite) TestSessionLoopWriteError(c *C) { |
3104 | @@ -357,7 +360,7 @@ |
3105 | go func() { |
3106 | errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack) |
3107 | }() |
3108 | - close(exchanges) |
3109 | + exchanges <- nil |
3110 | err := <-errCh |
3111 | c.Check(err, DeepEquals, &broker.ErrAbort{"terminated"}) |
3112 | } |
3113 | @@ -477,18 +480,44 @@ |
3114 | down := make(chan interface{}, 5) |
3115 | tp := &testProtocol{up, down} |
3116 | exchanges := make(chan broker.Exchange, 1) |
3117 | - exchanges <- &broker.ConnBrokenExchange{"REASON"} |
3118 | + msg := &protocol.ConnBrokenMsg{"connbroken", "BREASON"} |
3119 | + exchanges <- &broker.ConnMetaExchange{msg} |
3120 | sess := &testing.TestBrokerSession{Exchanges: exchanges} |
3121 | go func() { |
3122 | errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack) |
3123 | }() |
3124 | c.Check(takeNext(down), Equals, "deadline 2ms") |
3125 | - c.Check(takeNext(down), DeepEquals, protocol.ConnBrokenMsg{"connbroken", "REASON"}) |
3126 | + c.Check(takeNext(down), DeepEquals, protocol.ConnBrokenMsg{"connbroken", "BREASON"}) |
3127 | up <- nil // no write error |
3128 | err := <-errCh |
3129 | c.Check(err, DeepEquals, &broker.ErrAbort{"session broken for reason"}) |
3130 | } |
3131 | |
3132 | +func (s *sessionSuite) TestSessionLoopConnWarnExchange(c *C) { |
3133 | + nopTrack := NewTracker(s.testlog) |
3134 | + errCh := make(chan error, 1) |
3135 | + up := make(chan interface{}, 5) |
3136 | + down := make(chan interface{}, 5) |
3137 | + tp := &testProtocol{up, down} |
3138 | + exchanges := make(chan broker.Exchange, 1) |
3139 | + msg := &protocol.ConnWarnMsg{"connwarn", "WREASON"} |
3140 | + exchanges <- &broker.ConnMetaExchange{msg} |
3141 | + sess := &testing.TestBrokerSession{Exchanges: exchanges} |
3142 | + go func() { |
3143 | + errCh <- sessionLoop(tp, sess, cfg5msPingInterval2msExchangeTout, nopTrack) |
3144 | + }() |
3145 | + c.Check(takeNext(down), Equals, "deadline 2ms") |
3146 | + c.Check(takeNext(down), DeepEquals, protocol.ConnWarnMsg{"connwarn", "WREASON"}) |
3147 | + up <- nil // no write error |
3148 | + // session continues |
3149 | + c.Check(takeNext(down), Equals, "deadline 2ms") |
3150 | + c.Check(takeNext(down), DeepEquals, protocol.PingPongMsg{Type: "ping"}) |
3151 | + up <- nil // no write error |
3152 | + up <- io.EOF |
3153 | + err := <-errCh |
3154 | + c.Check(err, Equals, io.EOF) |
3155 | +} |
3156 | + |
3157 | type testTracker struct { |
3158 | SessionTracker |
3159 | interval chan interface{} |
3160 | @@ -593,7 +622,7 @@ |
3161 | msg, err = downStream.ReadBytes(byte('}')) |
3162 | c.Check(err, IsNil) |
3163 | c.Check(msg, DeepEquals, []byte("\x00\x0c{\"T\":\"ping\"}")) |
3164 | - c.Check(takeNext(brkr.registration), Equals, "register DEV") |
3165 | + c.Check(takeNext(brkr.registration), Equals, "register DEV "+track.SessionId()) |
3166 | c.Check(len(brkr.registration), Equals, 0) // not yet unregistered |
3167 | cli.Close() |
3168 | err = <-errCh |
3169 | |
3170 | === modified file 'server/session/tracker.go' |
3171 | --- server/session/tracker.go 2014-02-10 23:19:08 +0000 |
3172 | +++ server/session/tracker.go 2014-04-29 18:02:00 +0000 |
3173 | @@ -17,6 +17,7 @@ |
3174 | package session |
3175 | |
3176 | import ( |
3177 | + "fmt" |
3178 | "net" |
3179 | "time" |
3180 | |
3181 | @@ -29,6 +30,8 @@ |
3182 | logger.Logger |
3183 | // Session got started. |
3184 | Start(WithRemoteAddr) |
3185 | + // SessionId |
3186 | + SessionId() string |
3187 | // Session got registered with broker as sess BrokerSession. |
3188 | Registered(sess broker.BrokerSession) |
3189 | // Report effective elapsed ping interval. |
3190 | @@ -47,7 +50,7 @@ |
3191 | // Tracker implements SessionTracker simply. |
3192 | type tracker struct { |
3193 | logger.Logger |
3194 | - sessionId int64 // xxx use timeuuid later |
3195 | + sessionId string |
3196 | } |
3197 | |
3198 | func NewTracker(logger logger.Logger) SessionTracker { |
3199 | @@ -55,18 +58,22 @@ |
3200 | } |
3201 | |
3202 | func (trk *tracker) Start(conn WithRemoteAddr) { |
3203 | - trk.sessionId = time.Now().UnixNano() - sessionsEpoch |
3204 | - trk.Debugf("session(%x) connected %v", trk.sessionId, conn.RemoteAddr()) |
3205 | + trk.sessionId = fmt.Sprintf("%x", time.Now().UnixNano()-sessionsEpoch) |
3206 | + trk.Debugf("session(%s) connected %v", trk.sessionId, conn.RemoteAddr()) |
3207 | +} |
3208 | + |
3209 | +func (trk *tracker) SessionId() string { |
3210 | + return trk.sessionId |
3211 | } |
3212 | |
3213 | func (trk *tracker) Registered(sess broker.BrokerSession) { |
3214 | - trk.Infof("session(%x) registered %v", trk.sessionId, sess.DeviceIdentifier()) |
3215 | + trk.Infof("session(%s) registered %v", trk.sessionId, sess.DeviceIdentifier()) |
3216 | } |
3217 | |
3218 | func (trk *tracker) EffectivePingInterval(time.Duration) { |
3219 | } |
3220 | |
3221 | func (trk *tracker) End(err error) error { |
3222 | - trk.Debugf("session(%x) ended with: %v", trk.sessionId, err) |
3223 | + trk.Debugf("session(%s) ended with: %v", trk.sessionId, err) |
3224 | return err |
3225 | } |
3226 | |
3227 | === modified file 'server/session/tracker_test.go' |
3228 | --- server/session/tracker_test.go 2014-02-10 23:19:08 +0000 |
3229 | +++ server/session/tracker_test.go 2014-04-29 18:02:00 +0000 |
3230 | @@ -46,8 +46,8 @@ |
3231 | func (s *trackerSuite) TestSessionTrackStart(c *C) { |
3232 | track := NewTracker(s.testlog) |
3233 | track.Start(&testRemoteAddrable{}) |
3234 | - c.Check(track.(*tracker).sessionId, Not(Equals), 0) |
3235 | - regExpected := fmt.Sprintf(`DEBUG session\(%x\) connected 127\.0\.0\.1:9999\n`, track.(*tracker).sessionId) |
3236 | + c.Check(track.SessionId(), Not(Equals), "") |
3237 | + regExpected := fmt.Sprintf(`DEBUG session\(%s\) connected 127\.0\.0\.1:9999\n`, track.SessionId()) |
3238 | c.Check(s.testlog.Captured(), Matches, regExpected) |
3239 | } |
3240 | |
3241 | @@ -55,7 +55,7 @@ |
3242 | track := NewTracker(s.testlog) |
3243 | track.Start(&testRemoteAddrable{}) |
3244 | track.Registered(&testing.TestBrokerSession{DeviceId: "DEV-ID"}) |
3245 | - regExpected := fmt.Sprintf(`.*connected.*\nINFO session\(%x\) registered DEV-ID\n`, track.(*tracker).sessionId) |
3246 | + regExpected := fmt.Sprintf(`.*connected.*\nINFO session\(%s\) registered DEV-ID\n`, track.SessionId()) |
3247 | c.Check(s.testlog.Captured(), Matches, regExpected) |
3248 | } |
3249 | |
3250 | @@ -63,6 +63,6 @@ |
3251 | track := NewTracker(s.testlog) |
3252 | track.Start(&testRemoteAddrable{}) |
3253 | track.End(&broker.ErrAbort{}) |
3254 | - regExpected := fmt.Sprintf(`.*connected.*\nDEBUG session\(%x\) ended with: session aborted \(\)\n`, track.(*tracker).sessionId) |
3255 | + regExpected := fmt.Sprintf(`.*connected.*\nDEBUG session\(%s\) ended with: session aborted \(\)\n`, track.SessionId()) |
3256 | c.Check(s.testlog.Captured(), Matches, regExpected) |
3257 | } |
3258 | |
3259 | === modified file 'server/store/inmemory.go' |
3260 | --- server/store/inmemory.go 2014-02-18 14:19:05 +0000 |
3261 | +++ server/store/inmemory.go 2014-04-29 18:02:00 +0000 |
3262 | @@ -20,11 +20,13 @@ |
3263 | "encoding/json" |
3264 | "sync" |
3265 | "time" |
3266 | + |
3267 | + "launchpad.net/ubuntu-push/protocol" |
3268 | ) |
3269 | |
3270 | // one stored notification |
3271 | type notification struct { |
3272 | - payload json.RawMessage |
3273 | + protocol.Notification |
3274 | expiration time.Time |
3275 | } |
3276 | |
3277 | @@ -63,14 +65,14 @@ |
3278 | } |
3279 | prev.topLevel++ |
3280 | prev.notifications = append(prev.notifications, notification{ |
3281 | - payload: notificationPayload, |
3282 | - expiration: expiration, |
3283 | + Notification: protocol.Notification{Payload: notificationPayload}, |
3284 | + expiration: expiration, |
3285 | }) |
3286 | sto.store[chanId] = prev |
3287 | return nil |
3288 | } |
3289 | |
3290 | -func (sto *InMemoryPendingStore) GetChannelSnapshot(chanId InternalChannelId) (int64, []json.RawMessage, error) { |
3291 | +func (sto *InMemoryPendingStore) GetChannelSnapshot(chanId InternalChannelId) (int64, []protocol.Notification, error) { |
3292 | sto.lock.Lock() |
3293 | defer sto.lock.Unlock() |
3294 | channel, ok := sto.store[chanId] |
3295 | @@ -79,13 +81,13 @@ |
3296 | } |
3297 | topLevel := channel.topLevel |
3298 | n := len(channel.notifications) |
3299 | - res := make([]json.RawMessage, 0, n) |
3300 | + res := make([]protocol.Notification, 0, n) |
3301 | now := time.Now() |
3302 | for _, notification := range channel.notifications { |
3303 | if notification.expiration.Before(now) { |
3304 | continue |
3305 | } |
3306 | - res = append(res, notification.payload) |
3307 | + res = append(res, notification.Notification) |
3308 | } |
3309 | return topLevel, res, nil |
3310 | } |
3311 | |
3312 | === modified file 'server/store/inmemory_test.go' |
3313 | --- server/store/inmemory_test.go 2014-02-14 12:38:38 +0000 |
3314 | +++ server/store/inmemory_test.go 2014-04-29 18:02:00 +0000 |
3315 | @@ -21,6 +21,9 @@ |
3316 | "time" |
3317 | |
3318 | . "launchpad.net/gocheck" |
3319 | + |
3320 | + "launchpad.net/ubuntu-push/protocol" |
3321 | + help "launchpad.net/ubuntu-push/testing" |
3322 | ) |
3323 | |
3324 | type inMemorySuite struct{} |
3325 | @@ -45,7 +48,7 @@ |
3326 | top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId) |
3327 | c.Assert(err, IsNil) |
3328 | c.Check(top, Equals, int64(0)) |
3329 | - c.Check(res, DeepEquals, []json.RawMessage(nil)) |
3330 | + c.Check(res, DeepEquals, []protocol.Notification(nil)) |
3331 | } |
3332 | |
3333 | func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshot(c *C) { |
3334 | @@ -61,7 +64,7 @@ |
3335 | top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId) |
3336 | c.Assert(err, IsNil) |
3337 | c.Check(top, Equals, int64(2)) |
3338 | - c.Check(res, DeepEquals, []json.RawMessage{notification1, notification2}) |
3339 | + c.Check(res, DeepEquals, help.Ns(notification1, notification2)) |
3340 | } |
3341 | |
3342 | func (s *inMemorySuite) TestAppendToChannelAndGetChannelSnapshotWithExpiration(c *C) { |
3343 | @@ -81,5 +84,5 @@ |
3344 | top, res, err := sto.GetChannelSnapshot(SystemInternalChannelId) |
3345 | c.Assert(err, IsNil) |
3346 | c.Check(top, Equals, int64(2)) |
3347 | - c.Check(res, DeepEquals, []json.RawMessage{notification1}) |
3348 | + c.Check(res, DeepEquals, help.Ns(notification1)) |
3349 | } |
3350 | |
3351 | === modified file 'server/store/store.go' |
3352 | --- server/store/store.go 2014-02-18 13:43:07 +0000 |
3353 | +++ server/store/store.go 2014-04-29 18:02:00 +0000 |
3354 | @@ -22,6 +22,8 @@ |
3355 | "encoding/json" |
3356 | "errors" |
3357 | "time" |
3358 | + |
3359 | + "launchpad.net/ubuntu-push/protocol" |
3360 | ) |
3361 | |
3362 | type InternalChannelId string |
3363 | @@ -70,7 +72,7 @@ |
3364 | AppendToChannel(chanId InternalChannelId, notification json.RawMessage, expiration time.Time) error |
3365 | // GetChannelSnapshot gets all the current notifications and |
3366 | // current top level in the channel. |
3367 | - GetChannelSnapshot(chanId InternalChannelId) (topLevel int64, payloads []json.RawMessage, err error) |
3368 | + GetChannelSnapshot(chanId InternalChannelId) (topLevel int64, notifications []protocol.Notification, err error) |
3369 | // Close is to be called when done with the store. |
3370 | Close() |
3371 | } |
3372 | |
3373 | === modified file 'testing/helpers.go' |
3374 | --- testing/helpers.go 2014-02-21 16:04:44 +0000 |
3375 | +++ testing/helpers.go 2014-04-29 18:02:00 +0000 |
3376 | @@ -18,6 +18,7 @@ |
3377 | package testing |
3378 | |
3379 | import ( |
3380 | + "encoding/json" |
3381 | "fmt" |
3382 | "os" |
3383 | "path/filepath" |
3384 | @@ -26,6 +27,7 @@ |
3385 | "sync" |
3386 | |
3387 | "launchpad.net/ubuntu-push/logger" |
3388 | + "launchpad.net/ubuntu-push/protocol" |
3389 | ) |
3390 | |
3391 | type captureHelper struct { |
3392 | @@ -122,3 +124,12 @@ |
3393 | } |
3394 | return filepath.Join(dir, relativePath) |
3395 | } |
3396 | + |
3397 | +// Ns makes a []Notification from just payloads. |
3398 | +func Ns(payloads ...json.RawMessage) []protocol.Notification { |
3399 | + res := make([]protocol.Notification, len(payloads)) |
3400 | + for i := 0; i < len(payloads); i++ { |
3401 | + res[i].Payload = payloads[i] |
3402 | + } |
3403 | + return res |
3404 | +} |
3405 | |
3406 | === modified file 'ubuntu-push-client.go' |
3407 | --- ubuntu-push-client.go 2014-03-12 13:25:20 +0000 |
3408 | +++ ubuntu-push-client.go 2014-04-29 18:02:00 +0000 |
3409 | @@ -19,12 +19,38 @@ |
3410 | import ( |
3411 | "log" |
3412 | |
3413 | + "gopkg.in/qml.v0" |
3414 | + "launchpad.net/go-dbus/v1" |
3415 | "launchpad.net/go-xdg/v0" |
3416 | |
3417 | "launchpad.net/ubuntu-push/client" |
3418 | ) |
3419 | |
3420 | +const NAME = "com.ubuntu.PushNotifications" |
3421 | + |
3422 | +// grabName grabs ownership of the dbus name, and bails the client as |
3423 | +// soon as somebody else grabs it. |
3424 | +func grabName() { |
3425 | + conn, err := dbus.Connect(dbus.SessionBus) |
3426 | + if err != nil { |
3427 | + log.Fatalf("bus: %v", err) |
3428 | + } |
3429 | + |
3430 | + flags := dbus.NameFlagAllowReplacement | dbus.NameFlagReplaceExisting |
3431 | + n := conn.RequestName(NAME, flags) |
3432 | + go func() { |
3433 | + for err := range n.C { |
3434 | + if err != nil { |
3435 | + log.Fatalf("FATAL: name channel got: %v", err) |
3436 | + } |
3437 | + } |
3438 | + }() |
3439 | +} |
3440 | + |
3441 | func main() { |
3442 | + // XXX: this is a quick hack to ensure unicity |
3443 | + grabName() |
3444 | + |
3445 | cfgFname, err := xdg.Config.Find("ubuntu-push-client/config.json") |
3446 | if err != nil { |
3447 | log.Fatalf("unable to find a configuration file: %v", err) |
3448 | @@ -33,6 +59,9 @@ |
3449 | if err != nil { |
3450 | log.Fatalf("unable to open the levels database: %v", err) |
3451 | } |
3452 | + |
3453 | + qml.Init(nil) |
3454 | + |
3455 | cli := client.NewPushClient(cfgFname, lvlFname) |
3456 | err = cli.Start() |
3457 | if err != nil { |
3458 | |
3459 | === added file 'util/auth.go' |
3460 | --- util/auth.go 1970-01-01 00:00:00 +0000 |
3461 | +++ util/auth.go 2014-04-29 18:02:00 +0000 |
3462 | @@ -0,0 +1,36 @@ |
3463 | +/* |
3464 | + Copyright 2013-2014 Canonical Ltd. |
3465 | + |
3466 | + This program is free software: you can redistribute it and/or modify it |
3467 | + under the terms of the GNU General Public License version 3, as published |
3468 | + by the Free Software Foundation. |
3469 | + |
3470 | + This program is distributed in the hope that it will be useful, but |
3471 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
3472 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3473 | + PURPOSE. See the GNU General Public License for more details. |
3474 | + |
3475 | + You should have received a copy of the GNU General Public License along |
3476 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
3477 | +*/ |
3478 | + |
3479 | +package util |
3480 | + |
3481 | +import ( |
3482 | + "gopkg.in/niemeyer/uoneauth.v1" |
3483 | + "gopkg.in/qml.v0" |
3484 | +) |
3485 | + |
3486 | +func GetAuthorization() (string, error) { |
3487 | + engine := qml.NewEngine() |
3488 | + defer engine.Destroy() |
3489 | + authService := uoneauth.NewService(engine) |
3490 | + var auth string |
3491 | + token, err := authService.Token() |
3492 | + if err != nil { |
3493 | + return "", err |
3494 | + } else { |
3495 | + auth = token.HeaderSignature("POST", "https://push.ubuntu.com") |
3496 | + } |
3497 | + return auth, nil |
3498 | +} |
3499 | |
3500 | === added file 'util/auth_test.go' |
3501 | --- util/auth_test.go 1970-01-01 00:00:00 +0000 |
3502 | +++ util/auth_test.go 2014-04-29 18:02:00 +0000 |
3503 | @@ -0,0 +1,53 @@ |
3504 | +/* |
3505 | + Copyright 2013-2014 Canonical Ltd. |
3506 | + |
3507 | + This program is free software: you can redistribute it and/or modify it |
3508 | + under the terms of the GNU General Public License version 3, as published |
3509 | + by the Free Software Foundation. |
3510 | + |
3511 | + This program is distributed in the hope that it will be useful, but |
3512 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
3513 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3514 | + PURPOSE. See the GNU General Public License for more details. |
3515 | + |
3516 | + You should have received a copy of the GNU General Public License along |
3517 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
3518 | +*/ |
3519 | + |
3520 | +package util |
3521 | + |
3522 | +import ( |
3523 | + "os" |
3524 | + |
3525 | + "gopkg.in/qml.v0" |
3526 | + |
3527 | + . "launchpad.net/gocheck" |
3528 | +) |
3529 | + |
3530 | +type authSuite struct{} |
3531 | + |
3532 | +var _ = Suite(&authSuite{}) |
3533 | + |
3534 | +func (s *authSuite) SetUpSuite(c *C) { |
3535 | + if os.Getenv("PUSH_AUTH_TEST") == "1" { |
3536 | + qml.Init(nil) |
3537 | + } |
3538 | +} |
3539 | + |
3540 | +func (s *authSuite) SetUpTest(c *C) { |
3541 | + qml.SetLogger(c) |
3542 | +} |
3543 | + |
3544 | +func (s *authSuite) TestGetAuth(c *C) { |
3545 | + /* |
3546 | + * This test is only useful when the PUSH_AUTH_TEST environment |
3547 | + * variable is set to "1" - in which case the runner should have |
3548 | + * a Ubuntu One account setup via system-settings. |
3549 | + */ |
3550 | + if os.Getenv("PUSH_AUTH_TEST") != "1" { |
3551 | + c.Skip("PUSH_AUTH_TEST not set to '1'") |
3552 | + } |
3553 | + auth, err := GetAuthorization() |
3554 | + c.Assert(err, IsNil) |
3555 | + c.Assert(auth, Matches, "OAuth .*oauth_consumer_key=.*") |
3556 | +} |
3557 | |
3558 | === modified file 'whoopsie/identifier/identifier.go' |
3559 | --- whoopsie/identifier/identifier.go 2014-02-21 16:17:28 +0000 |
3560 | +++ whoopsie/identifier/identifier.go 2014-04-29 18:02:00 +0000 |
3561 | @@ -27,6 +27,7 @@ |
3562 | import "C" |
3563 | import "unsafe" |
3564 | import "errors" |
3565 | +import "time" |
3566 | |
3567 | // an Id knows how to generate itself, and how to stringify itself. |
3568 | type Id interface { |
3569 | @@ -36,12 +37,17 @@ |
3570 | |
3571 | // Identifier is the default Id implementation. |
3572 | type Identifier struct { |
3573 | - value string |
3574 | + value string |
3575 | + generator func(**C.char, **C.GError) |
3576 | +} |
3577 | + |
3578 | +func generator(csp **C.char, errp **C.GError) { |
3579 | + C.whoopsie_identifier_generate(csp, errp) |
3580 | } |
3581 | |
3582 | // New creates an Identifier, but does not call Generate() on it. |
3583 | func New() Id { |
3584 | - return &Identifier{} |
3585 | + return &Identifier{generator: generator} |
3586 | } |
3587 | |
3588 | // Generate makes the Identifier create the identifier itself. |
3589 | @@ -49,8 +55,18 @@ |
3590 | var gerr *C.GError |
3591 | var cs *C.char |
3592 | defer C.g_free((C.gpointer)(unsafe.Pointer(cs))) |
3593 | - C.whoopsie_identifier_generate(&cs, &gerr) |
3594 | - |
3595 | + |
3596 | + for i := 0; i < 200; i++ { |
3597 | + id.generator(&cs, &gerr) |
3598 | + |
3599 | + if cs != nil || gerr != nil { |
3600 | + goto SuccessMaybe |
3601 | + } |
3602 | + time.Sleep(600 * time.Millisecond) |
3603 | + } |
3604 | + return errors.New("whoopsie_identifier_generate still bad after 2m; giving up") |
3605 | + |
3606 | +SuccessMaybe: |
3607 | if gerr != nil { |
3608 | return errors.New(C.GoString((*C.char)(gerr.message))) |
3609 | } else { |
3610 | |
3611 | === modified file 'whoopsie/identifier/identifier_test.go' |
3612 | --- whoopsie/identifier/identifier_test.go 2014-01-15 15:51:50 +0000 |
3613 | +++ whoopsie/identifier/identifier_test.go 2014-04-29 18:02:00 +0000 |
3614 | @@ -41,3 +41,18 @@ |
3615 | func (s *IdentifierSuite) TestIdentifierInterface(c *C) { |
3616 | _ = []Id{New()} |
3617 | } |
3618 | + |
3619 | +// TestFailure checks that Identifier survives whoopsie shenanigans |
3620 | +func (s *IdentifierSuite) TestIdentifierSurvivesShenanigans(c *C) { |
3621 | + count := 0 |
3622 | + // using _Ctype* as a workaround for gocheck also having a C |
3623 | + gen := func(csp **_Ctype_char, errp **_Ctype_GError) { |
3624 | + count++ |
3625 | + if count > 3 { |
3626 | + generator(csp, errp) |
3627 | + } |
3628 | + } |
3629 | + id := &Identifier{generator: gen} |
3630 | + id.Generate() |
3631 | + c.Check(id.String(), HasLen, 128) |
3632 | +} |