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