Merge lp:~pedronis/ubuntu-push/robust-tests-1.3 into lp:ubuntu-push
- robust-tests-1.3
- Merge into trunk
Proposed by
Samuele Pedroni
Status: | Superseded |
---|---|
Proposed branch: | lp:~pedronis/ubuntu-push/robust-tests-1.3 |
Merge into: | lp:ubuntu-push |
Diff against target: |
841 lines (+219/-90) 19 files modified
.precommit (+21/-19) PACKAGE_DEPS (+1/-0) client/service/service.go (+2/-0) client/service/service_test.go (+28/-5) client/session/session.go (+3/-3) client/session/session_test.go (+5/-5) debian/config.json (+1/-1) docs/_common.txt (+43/-0) docs/example-client/main.qml (+34/-17) docs/example-client/manifest.json (+1/-1) docs/lowlevel.txt (+2/-0) launch_helper/kindpool.go (+3/-2) launch_helper/legacy/legacy.go (+3/-4) messaging/messaging_test.go (+14/-7) server/acceptance/suites/helpers.go (+7/-6) server/api/handlers.go (+15/-1) server/api/handlers_test.go (+22/-6) server/broker/testsuite/suite.go (+11/-11) server/listener/listener_test.go (+3/-2) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/robust-tests-1.3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email:
|
Commit message
make tests more robust in the face of 1.3
Description of the change
make tests more robust in the face of 1.3
- avoid map order issues
- -race slows down some tests so that small timeouts were problematic => increase timeout
- Reader Read can return data and EOF error together => use helpers
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 '.precommit' | |||
2 | --- .precommit 2014-01-23 10:03:39 +0000 | |||
3 | +++ .precommit 2014-11-25 18:40:27 +0000 | |||
4 | @@ -5,25 +5,27 @@ | |||
5 | 5 | # And put this here-document in ~/.bazaar/plugins/precommit_script.py: | 5 | # And put this here-document in ~/.bazaar/plugins/precommit_script.py: |
6 | 6 | <<EOF | 6 | <<EOF |
7 | 7 | import os | 7 | import os |
27 | 8 | import subprocess | 8 | |
28 | 9 | from bzrlib.mutabletree import MutableTree | 9 | if not os.getenv("SKIP_COMMIT_HOOK"): |
29 | 10 | from bzrlib import errors | 10 | import subprocess |
30 | 11 | 11 | from bzrlib.mutabletree import MutableTree | |
31 | 12 | def start_commit_hook(*_): | 12 | from bzrlib import errors |
32 | 13 | """This hook will execute '.precommit' script from root path of the bazaar | 13 | |
33 | 14 | branch. Commit will be canceled if precommit fails.""" | 14 | def start_commit_hook(*_): |
34 | 15 | 15 | """This hook will execute '.precommit' script from root path of the bazaar | |
35 | 16 | # this hook only makes sense if a precommit file exist. | 16 | branch. Commit will be canceled if precommit fails.""" |
36 | 17 | if not os.path.exists(".precommit"): | 17 | |
37 | 18 | return | 18 | # this hook only makes sense if a precommit file exist. |
38 | 19 | try: | 19 | if not os.path.exists(".precommit"): |
39 | 20 | subprocess.check_call(os.path.abspath(".precommit")) | 20 | return |
40 | 21 | # if precommit fails (process return not zero) cancel commit. | 21 | try: |
41 | 22 | except subprocess.CalledProcessError: | 22 | subprocess.check_call(os.path.abspath(".precommit")) |
42 | 23 | raise errors.BzrError("pre commit check failed.") | 23 | # if precommit fails (process return not zero) cancel commit. |
43 | 24 | 24 | except subprocess.CalledProcessError: | |
44 | 25 | MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, | 25 | raise errors.BzrError("pre commit check failed (set SKIP_COMMIT_HOOK to skip).") |
45 | 26 | 'Run "precommit" script on start_commit') | 26 | |
46 | 27 | MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, | ||
47 | 28 | 'Run "precommit" script on start_commit') | ||
48 | 27 | EOF | 29 | EOF |
49 | 28 | 30 | ||
50 | 29 | make check-format # or whatever | 31 | make check-format # or whatever |
51 | 30 | 32 | ||
52 | === modified file 'PACKAGE_DEPS' | |||
53 | --- PACKAGE_DEPS 2014-09-05 10:48:36 +0000 | |||
54 | +++ PACKAGE_DEPS 2014-11-25 18:40:27 +0000 | |||
55 | @@ -12,3 +12,4 @@ | |||
56 | 12 | libclick-0.4-dev | 12 | libclick-0.4-dev |
57 | 13 | liburl-dispatcher1-dev | 13 | liburl-dispatcher1-dev |
58 | 14 | libaccounts-glib-dev | 14 | libaccounts-glib-dev |
59 | 15 | system-image-dbus | ||
60 | 15 | 16 | ||
61 | === modified file 'client/service/service.go' | |||
62 | --- client/service/service.go 2014-11-03 13:36:00 +0000 | |||
63 | +++ client/service/service.go 2014-11-25 18:40:27 +0000 | |||
64 | @@ -140,6 +140,8 @@ | |||
65 | 140 | case resp.StatusCode >= http.StatusInternalServerError: | 140 | case resp.StatusCode >= http.StatusInternalServerError: |
66 | 141 | // XXX retry on 503 | 141 | // XXX retry on 503 |
67 | 142 | return nil, ErrBadServer | 142 | return nil, ErrBadServer |
68 | 143 | case resp.StatusCode == http.StatusUnauthorized: | ||
69 | 144 | return nil, ErrBadAuth | ||
70 | 143 | default: | 145 | default: |
71 | 144 | return nil, ErrBadRequest | 146 | return nil, ErrBadRequest |
72 | 145 | } | 147 | } |
73 | 146 | 148 | ||
74 | === modified file 'client/service/service_test.go' | |||
75 | --- client/service/service_test.go 2014-08-06 09:01:59 +0000 | |||
76 | +++ client/service/service_test.go 2014-11-25 18:40:27 +0000 | |||
77 | @@ -19,6 +19,7 @@ | |||
78 | 19 | import ( | 19 | import ( |
79 | 20 | "encoding/json" | 20 | "encoding/json" |
80 | 21 | "fmt" | 21 | "fmt" |
81 | 22 | "io" | ||
82 | 22 | "net/http" | 23 | "net/http" |
83 | 23 | "net/http/httptest" | 24 | "net/http/httptest" |
84 | 24 | "os" | 25 | "os" |
85 | @@ -179,7 +180,8 @@ | |||
86 | 179 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { | 180 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { |
87 | 180 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 181 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
88 | 181 | buf := make([]byte, 256) | 182 | buf := make([]byte, 256) |
90 | 182 | n, e := r.Body.Read(buf) | 183 | n := r.ContentLength |
91 | 184 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
92 | 183 | c.Assert(e, IsNil) | 185 | c.Assert(e, IsNil) |
93 | 184 | req := registrationRequest{} | 186 | req := registrationRequest{} |
94 | 185 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 187 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
95 | @@ -240,6 +242,23 @@ | |||
96 | 240 | c.Check(err, ErrorMatches, "unable to request registration: .*") | 242 | c.Check(err, ErrorMatches, "unable to request registration: .*") |
97 | 241 | } | 243 | } |
98 | 242 | 244 | ||
99 | 245 | func (ss *serviceSuite) TestManageRegFailsOn401(c *C) { | ||
100 | 246 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
101 | 247 | http.Error(w, "Unauthorized", 401) | ||
102 | 248 | })) | ||
103 | 249 | defer ts.Close() | ||
104 | 250 | setup := &PushServiceSetup{ | ||
105 | 251 | DeviceId: "fake-device-id", | ||
106 | 252 | RegURL: helpers.ParseURL(ts.URL), | ||
107 | 253 | AuthGetter: func(string) string { return "tok" }, | ||
108 | 254 | } | ||
109 | 255 | svc := NewPushService(setup, ss.log) | ||
110 | 256 | svc.Bus = ss.bus | ||
111 | 257 | reg, err := svc.register(aPackageOnBus, []interface{}{anAppId}, nil) | ||
112 | 258 | c.Check(err, Equals, ErrBadAuth) | ||
113 | 259 | c.Check(reg, IsNil) | ||
114 | 260 | } | ||
115 | 261 | |||
116 | 243 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { | 262 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { |
117 | 244 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 263 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
118 | 245 | http.Error(w, "I'm a teapot", 418) | 264 | http.Error(w, "I'm a teapot", 418) |
119 | @@ -277,7 +296,8 @@ | |||
120 | 277 | func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { | 296 | func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { |
121 | 278 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 297 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
122 | 279 | buf := make([]byte, 256) | 298 | buf := make([]byte, 256) |
124 | 280 | n, e := r.Body.Read(buf) | 299 | n := r.ContentLength |
125 | 300 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
126 | 281 | c.Assert(e, IsNil) | 301 | c.Assert(e, IsNil) |
127 | 282 | req := registrationRequest{} | 302 | req := registrationRequest{} |
128 | 283 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 303 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
129 | @@ -303,7 +323,8 @@ | |||
130 | 303 | func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { | 323 | func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { |
131 | 304 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 324 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
132 | 305 | buf := make([]byte, 256) | 325 | buf := make([]byte, 256) |
134 | 306 | n, e := r.Body.Read(buf) | 326 | n := r.ContentLength |
135 | 327 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
136 | 307 | c.Assert(e, IsNil) | 328 | c.Assert(e, IsNil) |
137 | 308 | req := registrationRequest{} | 329 | req := registrationRequest{} |
138 | 309 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 330 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
139 | @@ -329,7 +350,8 @@ | |||
140 | 329 | func (ss *serviceSuite) TestDBusUnregisterWorks(c *C) { | 350 | func (ss *serviceSuite) TestDBusUnregisterWorks(c *C) { |
141 | 330 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 351 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
142 | 331 | buf := make([]byte, 256) | 352 | buf := make([]byte, 256) |
144 | 332 | n, e := r.Body.Read(buf) | 353 | n := r.ContentLength |
145 | 354 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
146 | 333 | c.Assert(e, IsNil) | 355 | c.Assert(e, IsNil) |
147 | 334 | req := registrationRequest{} | 356 | req := registrationRequest{} |
148 | 335 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 357 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
149 | @@ -356,7 +378,8 @@ | |||
150 | 356 | invoked := make(chan bool, 1) | 378 | invoked := make(chan bool, 1) |
151 | 357 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 379 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
152 | 358 | buf := make([]byte, 256) | 380 | buf := make([]byte, 256) |
154 | 359 | n, e := r.Body.Read(buf) | 381 | n := r.ContentLength |
155 | 382 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
156 | 360 | c.Assert(e, IsNil) | 383 | c.Assert(e, IsNil) |
157 | 361 | req := registrationRequest{} | 384 | req := registrationRequest{} |
158 | 362 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 385 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
159 | 363 | 386 | ||
160 | === modified file 'client/session/session.go' | |||
161 | --- client/session/session.go 2014-11-03 13:36:00 +0000 | |||
162 | +++ client/session/session.go 2014-11-25 18:40:27 +0000 | |||
163 | @@ -430,7 +430,7 @@ | |||
164 | 430 | return err | 430 | return err |
165 | 431 | } | 431 | } |
166 | 432 | sess.clearShouldDelay() | 432 | sess.clearShouldDelay() |
168 | 433 | sess.Log.Debugf("broadcast chan:%v app:%v topLevel:%d payloads:%s", | 433 | sess.Log.Infof("broadcast chan:%v app:%v topLevel:%d payloads:%s", |
169 | 434 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) | 434 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) |
170 | 435 | if bcast.ChanId == protocol.SystemChannelId { | 435 | if bcast.ChanId == protocol.SystemChannelId { |
171 | 436 | // the system channel id, the only one we care about for now | 436 | // the system channel id, the only one we care about for now |
172 | @@ -438,7 +438,7 @@ | |||
173 | 438 | sess.BroadcastCh <- sess.decodeBroadcast(bcast) | 438 | sess.BroadcastCh <- sess.decodeBroadcast(bcast) |
174 | 439 | sess.Log.Debugf("sent bcast over") | 439 | sess.Log.Debugf("sent bcast over") |
175 | 440 | } else { | 440 | } else { |
177 | 441 | sess.Log.Debugf("what is this weird channel, %#v?", bcast.ChanId) | 441 | sess.Log.Errorf("what is this weird channel, %#v?", bcast.ChanId) |
178 | 442 | } | 442 | } |
179 | 443 | return nil | 443 | return nil |
180 | 444 | } | 444 | } |
181 | @@ -468,7 +468,7 @@ | |||
182 | 468 | if to == nil { | 468 | if to == nil { |
183 | 469 | continue | 469 | continue |
184 | 470 | } | 470 | } |
186 | 471 | sess.Log.Debugf("unicast app:%v msg:%s payload:%s", | 471 | sess.Log.Infof("unicast app:%v msg:%s payload:%s", |
187 | 472 | notif.AppId, notif.MsgId, notif.Payload) | 472 | notif.AppId, notif.MsgId, notif.Payload) |
188 | 473 | sess.Log.Debugf("sending ucast over") | 473 | sess.Log.Debugf("sending ucast over") |
189 | 474 | sess.NotificationsCh <- AddressedNotification{to, notif} | 474 | sess.NotificationsCh <- AddressedNotification{to, notif} |
190 | 475 | 475 | ||
191 | === modified file 'client/session/session_test.go' | |||
192 | --- client/session/session_test.go 2014-11-03 13:36:00 +0000 | |||
193 | +++ client/session/session_test.go 2014-11-25 18:40:27 +0000 | |||
194 | @@ -1480,7 +1480,7 @@ | |||
195 | 1480 | } | 1480 | } |
196 | 1481 | 1481 | ||
197 | 1482 | var ( | 1482 | var ( |
199 | 1483 | dialTestTimeout = 100 * time.Millisecond | 1483 | dialTestTimeout = 300 * time.Millisecond |
200 | 1484 | dialTestConf = ClientSessionConfig{ | 1484 | dialTestConf = ClientSessionConfig{ |
201 | 1485 | ExchangeTimeout: dialTestTimeout, | 1485 | ExchangeTimeout: dialTestTimeout, |
202 | 1486 | PEM: helpers.TestCertPEMBlock, | 1486 | PEM: helpers.TestCertPEMBlock, |
203 | @@ -1584,7 +1584,7 @@ | |||
204 | 1584 | 1584 | ||
205 | 1585 | // 2. "connect" (but on the fake protcol above! woo) | 1585 | // 2. "connect" (but on the fake protcol above! woo) |
206 | 1586 | 1586 | ||
208 | 1587 | c.Check(takeNext(downCh), Equals, "deadline 100ms") | 1587 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout)) |
209 | 1588 | _, ok := takeNext(downCh).(protocol.ConnectMsg) | 1588 | _, ok := takeNext(downCh).(protocol.ConnectMsg) |
210 | 1589 | c.Check(ok, Equals, true) | 1589 | c.Check(ok, Equals, true) |
211 | 1590 | upCh <- nil // no error | 1590 | upCh <- nil // no error |
212 | @@ -1597,7 +1597,7 @@ | |||
213 | 1597 | // 3. "loop" | 1597 | // 3. "loop" |
214 | 1598 | 1598 | ||
215 | 1599 | // ping works, | 1599 | // ping works, |
217 | 1600 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1600 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
218 | 1601 | upCh <- protocol.PingPongMsg{Type: "ping"} | 1601 | upCh <- protocol.PingPongMsg{Type: "ping"} |
219 | 1602 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1602 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
220 | 1603 | upCh <- nil | 1603 | upCh <- nil |
221 | @@ -1613,7 +1613,7 @@ | |||
222 | 1613 | TopLevel: 2, | 1613 | TopLevel: 2, |
223 | 1614 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 1614 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
224 | 1615 | } | 1615 | } |
226 | 1616 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1616 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
227 | 1617 | upCh <- b | 1617 | upCh <- b |
228 | 1618 | c.Check(takeNext(downCh), Equals, protocol.AckMsg{"ack"}) | 1618 | c.Check(takeNext(downCh), Equals, protocol.AckMsg{"ack"}) |
229 | 1619 | upCh <- nil | 1619 | upCh <- nil |
230 | @@ -1625,7 +1625,7 @@ | |||
231 | 1625 | c.Check(levels, DeepEquals, map[string]int64{"0": 2}) | 1625 | c.Check(levels, DeepEquals, map[string]int64{"0": 2}) |
232 | 1626 | 1626 | ||
233 | 1627 | // and ping still work even after that. | 1627 | // and ping still work even after that. |
235 | 1628 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1628 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
236 | 1629 | upCh <- protocol.PingPongMsg{Type: "ping"} | 1629 | upCh <- protocol.PingPongMsg{Type: "ping"} |
237 | 1630 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1630 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
238 | 1631 | failure := errors.New("pongs") | 1631 | failure := errors.New("pongs") |
239 | 1632 | 1632 | ||
240 | === modified file 'debian/config.json' | |||
241 | --- debian/config.json 2014-08-21 10:47:12 +0000 | |||
242 | +++ debian/config.json 2014-11-25 18:40:27 +0000 | |||
243 | @@ -12,7 +12,7 @@ | |||
244 | 12 | "recheck_timeout": "10m", | 12 | "recheck_timeout": "10m", |
245 | 13 | "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html", | 13 | "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html", |
246 | 14 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", | 14 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", |
248 | 15 | "log_level": "debug", | 15 | "log_level": "info", |
249 | 16 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, | 16 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, |
250 | 17 | "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", | 17 | "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", |
251 | 18 | "poll_interval": "5m", | 18 | "poll_interval": "5m", |
252 | 19 | 19 | ||
253 | === modified file 'docs/_common.txt' | |||
254 | --- docs/_common.txt 2014-10-09 16:06:52 +0000 | |||
255 | +++ docs/_common.txt 2014-11-25 18:40:27 +0000 | |||
256 | @@ -185,3 +185,46 @@ | |||
257 | 185 | :clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error. | 185 | :clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error. |
258 | 186 | :replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one. | 186 | :replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one. |
259 | 187 | :data: A JSON object. | 187 | :data: A JSON object. |
260 | 188 | |||
261 | 189 | Limitations of the Server API | ||
262 | 190 | ----------------------------- | ||
263 | 191 | |||
264 | 192 | The push notification infrastructure is meant to help ensuring timely | ||
265 | 193 | delivery of application notifications if the device is online or | ||
266 | 194 | timely informing the device user about application notifications that | ||
267 | 195 | were pending when the device comes back online. This in the face of | ||
268 | 196 | applications not being allowed to be running all the time, and | ||
269 | 197 | avoiding the resource cost of many applications all polling different services | ||
270 | 198 | frequently. | ||
271 | 199 | |||
272 | 200 | The push notification infrastructure is architected to guarantee at | ||
273 | 201 | least best-effort with respect to these goals and beyond it, on the | ||
274 | 202 | other end applications should not expect to be able to use and only | ||
275 | 203 | rely on the push notification infrastructure to store application | ||
276 | 204 | messages if they want ensure all their notification or messages are | ||
277 | 205 | delivered, the infrastructure is not intended to be the only long term | ||
278 | 206 | "inbox" storage for an application. | ||
279 | 207 | |||
280 | 208 | To preserve overall throughput the infrastructure imposes some limits | ||
281 | 209 | on applications: | ||
282 | 210 | |||
283 | 211 | * message data payload is limited to 2K | ||
284 | 212 | |||
285 | 213 | * when inserted all messages need to specify an expiration date after | ||
286 | 214 | which they can be dropped and not delivered | ||
287 | 215 | |||
288 | 216 | * an application is limited in the number of messages per token | ||
289 | 217 | (application/user/device combination) that can be undelivered/pending at the | ||
290 | 218 | same time (100 currently) | ||
291 | 219 | |||
292 | 220 | replace_tag can be used to implement notifications for which the newest | ||
293 | 221 | one replace the previous one if pending. | ||
294 | 222 | |||
295 | 223 | clear_pending can be used to be deal with a pending message limit | ||
296 | 224 | reached, possibly substituting the current undelivered messages with a | ||
297 | 225 | more generic one. | ||
298 | 226 | |||
299 | 227 | Applications using the push notification HTTP API should be robust | ||
300 | 228 | against receiving 503 errors, retrying after waiting with increasing | ||
301 | 229 | back-off. Later rate limits (signaled with the 429 status) may also come | ||
302 | 230 | into play. | ||
303 | 188 | 231 | ||
304 | === modified file 'docs/example-client/main.qml' | |||
305 | --- docs/example-client/main.qml 2014-09-10 14:38:40 +0000 | |||
306 | +++ docs/example-client/main.qml 2014-11-25 18:40:27 +0000 | |||
307 | @@ -26,9 +26,42 @@ | |||
308 | 26 | property alias nickEnabled: nickEdit.enabled | 26 | property alias nickEnabled: nickEdit.enabled |
309 | 27 | } | 27 | } |
310 | 28 | 28 | ||
311 | 29 | states: [ | ||
312 | 30 | State { | ||
313 | 31 | name: "no-push-token" | ||
314 | 32 | when: (pushClient.token == "") | ||
315 | 33 | PropertyChanges { target: nickEdit; readOnly: true} | ||
316 | 34 | PropertyChanges { target: nickEdit; focus: true} | ||
317 | 35 | PropertyChanges { target: messageEdit; enabled: false} | ||
318 | 36 | PropertyChanges { target: loginButton; enabled: false} | ||
319 | 37 | PropertyChanges { target: loginButton; text: "Login"} | ||
320 | 38 | }, | ||
321 | 39 | State { | ||
322 | 40 | name: "push-token-not-registered" | ||
323 | 41 | when: ((pushClient.token != "") && (chatClient.registered == false)) | ||
324 | 42 | PropertyChanges { target: nickEdit; readOnly: false} | ||
325 | 43 | PropertyChanges { target: nickEdit; text: ""} | ||
326 | 44 | PropertyChanges { target: nickEdit; focus: true} | ||
327 | 45 | PropertyChanges { target: messageEdit; enabled: false} | ||
328 | 46 | PropertyChanges { target: loginButton; enabled: true} | ||
329 | 47 | PropertyChanges { target: loginButton; text: "Login"} | ||
330 | 48 | }, | ||
331 | 49 | State { | ||
332 | 50 | name: "registered" | ||
333 | 51 | when: ((pushClient.token != "") && (chatClient.registered == true)) | ||
334 | 52 | PropertyChanges { target: nickEdit; readOnly: true} | ||
335 | 53 | PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick} | ||
336 | 54 | PropertyChanges { target: messageEdit; focus: true} | ||
337 | 55 | PropertyChanges { target: messageEdit; enabled: true} | ||
338 | 56 | PropertyChanges { target: loginButton; enabled: true} | ||
339 | 57 | PropertyChanges { target: loginButton; text: "Logout"} | ||
340 | 58 | } | ||
341 | 59 | ] | ||
342 | 60 | |||
343 | 61 | state: "no-push-token" | ||
344 | 62 | |||
345 | 29 | ChatClient { | 63 | ChatClient { |
346 | 30 | id: chatClient | 64 | id: chatClient |
347 | 31 | onRegisteredChanged: {nickEdit.registered()} | ||
348 | 32 | onError: {messageList.handle_error(msg)} | 65 | onError: {messageList.handle_error(msg)} |
349 | 33 | token: pushClient.token | 66 | token: pushClient.token |
350 | 34 | } | 67 | } |
351 | @@ -44,7 +77,6 @@ | |||
352 | 44 | 77 | ||
353 | 45 | TextField { | 78 | TextField { |
354 | 46 | id: nickEdit | 79 | id: nickEdit |
355 | 47 | focus: true | ||
356 | 48 | placeholderText: "Your nickname" | 80 | placeholderText: "Your nickname" |
357 | 49 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase | 81 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase |
358 | 50 | anchors.left: parent.left | 82 | anchors.left: parent.left |
359 | @@ -53,31 +85,17 @@ | |||
360 | 53 | anchors.leftMargin: units.gu(.5) | 85 | anchors.leftMargin: units.gu(.5) |
361 | 54 | anchors.rightMargin: units.gu(1) | 86 | anchors.rightMargin: units.gu(1) |
362 | 55 | anchors.topMargin: units.gu(.5) | 87 | anchors.topMargin: units.gu(.5) |
363 | 56 | function registered() { | ||
364 | 57 | readOnly = true | ||
365 | 58 | text = "Your nick is " + chatClient.nick | ||
366 | 59 | messageEdit.focus = true | ||
367 | 60 | messageEdit.enabled = true | ||
368 | 61 | loginButton.text = "Logout" | ||
369 | 62 | } | ||
370 | 63 | onAccepted: { loginButton.clicked() } | 88 | onAccepted: { loginButton.clicked() } |
371 | 64 | } | 89 | } |
372 | 65 | 90 | ||
373 | 66 | Button { | 91 | Button { |
374 | 67 | id: loginButton | 92 | id: loginButton |
375 | 68 | text: chatClient.rgistered? "Logout": "Login" | ||
376 | 69 | anchors.top: nickEdit.top | 93 | anchors.top: nickEdit.top |
377 | 70 | anchors.right: parent.right | 94 | anchors.right: parent.right |
378 | 71 | anchors.rightMargin: units.gu(.5) | 95 | anchors.rightMargin: units.gu(.5) |
379 | 72 | onClicked: { | 96 | onClicked: { |
380 | 73 | if (chatClient.nick) { // logout | 97 | if (chatClient.nick) { // logout |
381 | 74 | chatClient.nick = "" | 98 | chatClient.nick = "" |
382 | 75 | text = "Login" | ||
383 | 76 | nickEdit.enabled = true | ||
384 | 77 | nickEdit.readOnly = false | ||
385 | 78 | nickEdit.text = "" | ||
386 | 79 | nickEdit.focus = true | ||
387 | 80 | messageEdit.enabled = false | ||
388 | 81 | } else { // login | 99 | } else { // login |
389 | 82 | chatClient.nick = nickEdit.text | 100 | chatClient.nick = nickEdit.text |
390 | 83 | } | 101 | } |
391 | @@ -94,7 +112,6 @@ | |||
392 | 94 | anchors.rightMargin: units.gu(.5) | 112 | anchors.rightMargin: units.gu(.5) |
393 | 95 | anchors.leftMargin: units.gu(.5) | 113 | anchors.leftMargin: units.gu(.5) |
394 | 96 | placeholderText: "Your message" | 114 | placeholderText: "Your message" |
395 | 97 | enabled: false | ||
396 | 98 | onAccepted: { | 115 | onAccepted: { |
397 | 99 | console.log("sending " + text) | 116 | console.log("sending " + text) |
398 | 100 | var idx = text.indexOf(":") | 117 | var idx = text.indexOf(":") |
399 | 101 | 118 | ||
400 | === modified file 'docs/example-client/manifest.json' | |||
401 | --- docs/example-client/manifest.json 2014-09-10 14:38:31 +0000 | |||
402 | +++ docs/example-client/manifest.json 2014-11-25 18:40:27 +0000 | |||
403 | @@ -15,5 +15,5 @@ | |||
404 | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", |
405 | 16 | "name": "com.ubuntu.developer.ralsina.hello", | 16 | "name": "com.ubuntu.developer.ralsina.hello", |
406 | 17 | "title": "Hello", | 17 | "title": "Hello", |
408 | 18 | "version": "0.4.2" | 18 | "version": "0.4.3" |
409 | 19 | } | 19 | } |
410 | 20 | 20 | ||
411 | === modified file 'docs/lowlevel.txt' | |||
412 | --- docs/lowlevel.txt 2014-10-15 16:34:25 +0000 | |||
413 | +++ docs/lowlevel.txt 2014-11-25 18:40:27 +0000 | |||
414 | @@ -57,6 +57,8 @@ | |||
415 | 57 | The Register method takes as argument the APP_ID (in the example, com.ubuntu.music_music) and returns a token identifying the user | 57 | The Register method takes as argument the APP_ID (in the example, com.ubuntu.music_music) and returns a token identifying the user |
416 | 58 | and device. For this to succeed the user **must** have an Ubuntu One account configured in the device. | 58 | and device. For this to succeed the user **must** have an Ubuntu One account configured in the device. |
417 | 59 | 59 | ||
418 | 60 | In the case the Register method returns a "bad auth" error, the application should inform the user to generate new Ubuntu One tokens. | ||
419 | 61 | |||
420 | 60 | The APP_ID is as described in the `ApplicationId documentation <https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId>`__ | 62 | The APP_ID is as described in the `ApplicationId documentation <https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId>`__ |
421 | 61 | except that the version is treated as optional. Therefore both ``com.ubuntu.music_music`` and ``com.ubuntu.music_music_1.3.496`` | 63 | except that the version is treated as optional. Therefore both ``com.ubuntu.music_music`` and ``com.ubuntu.music_music_1.3.496`` |
422 | 62 | are valid. Keep in mind that while both versioned and unversioned APP_IDs are valid, they are still different and will affect | 64 | are valid. Keep in mind that while both versioned and unversioned APP_IDs are valid, they are still different and will affect |
423 | 63 | 65 | ||
424 | === modified file 'launch_helper/kindpool.go' | |||
425 | --- launch_helper/kindpool.go 2014-08-20 12:42:51 +0000 | |||
426 | +++ launch_helper/kindpool.go 2014-11-25 18:40:27 +0000 | |||
427 | @@ -294,12 +294,13 @@ | |||
428 | 294 | } | 294 | } |
429 | 295 | payload, err := ioutil.ReadFile(args.FileOut) | 295 | payload, err := ioutil.ReadFile(args.FileOut) |
430 | 296 | if err != nil { | 296 | if err != nil { |
432 | 297 | pool.log.Errorf("unable to read output from helper: %v", err) | 297 | pool.log.Errorf("unable to read output from %v helper: %v", args.AppId, err) |
433 | 298 | } else { | 298 | } else { |
434 | 299 | pool.log.Infof("%v helper output: %#v", args.AppId, payload) | ||
435 | 299 | res := &HelperResult{Input: args.Input} | 300 | res := &HelperResult{Input: args.Input} |
436 | 300 | err = json.Unmarshal(payload, &res.HelperOutput) | 301 | err = json.Unmarshal(payload, &res.HelperOutput) |
437 | 301 | if err != nil { | 302 | if err != nil { |
439 | 302 | pool.log.Debugf("failed to parse HelperOutput from helper output: %v", err) | 303 | pool.log.Errorf("failed to parse HelperOutput from %v helper output: %v", args.AppId, err) |
440 | 303 | } else { | 304 | } else { |
441 | 304 | pool.chOut <- res | 305 | pool.chOut <- res |
442 | 305 | } | 306 | } |
443 | 306 | 307 | ||
444 | === modified file 'launch_helper/legacy/legacy.go' | |||
445 | --- launch_helper/legacy/legacy.go 2014-08-21 18:03:49 +0000 | |||
446 | +++ launch_helper/legacy/legacy.go 2014-11-25 18:40:27 +0000 | |||
447 | @@ -55,7 +55,7 @@ | |||
448 | 55 | err error | 55 | err error |
449 | 56 | } | 56 | } |
450 | 57 | 57 | ||
452 | 58 | func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) { | 58 | func (lhl *legacyHelperLauncher) Launch(appId, progname, f1, f2 string) (string, error) { |
453 | 59 | comm := make(chan msg) | 59 | comm := make(chan msg) |
454 | 60 | 60 | ||
455 | 61 | go func() { | 61 | go func() { |
456 | @@ -78,9 +78,8 @@ | |||
457 | 78 | p_err := cmd.Wait() | 78 | p_err := cmd.Wait() |
458 | 79 | if p_err != nil { | 79 | if p_err != nil { |
459 | 80 | // Helper failed or got killed, log output/errors | 80 | // Helper failed or got killed, log output/errors |
463 | 81 | lhl.log.Errorf("Legacy helper failed: %v", p_err) | 81 | lhl.log.Errorf("Legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.", |
464 | 82 | lhl.log.Errorf("Legacy helper failed. Stdout: %s", stdout) | 82 | appId, progname, id, p_err, stdout.String(), stderr.String()) |
462 | 83 | lhl.log.Errorf("Legacy helper failed. Stderr: %s", stderr) | ||
465 | 84 | } | 83 | } |
466 | 85 | lhl.done(id) | 84 | lhl.done(id) |
467 | 86 | }() | 85 | }() |
468 | 87 | 86 | ||
469 | === modified file 'messaging/messaging_test.go' | |||
470 | --- messaging/messaging_test.go 2014-07-27 02:54:40 +0000 | |||
471 | +++ messaging/messaging_test.go 2014-11-25 18:40:27 +0000 | |||
472 | @@ -17,6 +17,7 @@ | |||
473 | 17 | package messaging | 17 | package messaging |
474 | 18 | 18 | ||
475 | 19 | import ( | 19 | import ( |
476 | 20 | "sort" | ||
477 | 20 | "time" | 21 | "time" |
478 | 21 | 22 | ||
479 | 22 | . "launchpad.net/gocheck" | 23 | . "launchpad.net/gocheck" |
480 | @@ -121,6 +122,12 @@ | |||
481 | 121 | c.Check(payload.Actions[1], Equals, "action-1") | 122 | c.Check(payload.Actions[1], Equals, "action-1") |
482 | 122 | } | 123 | } |
483 | 123 | 124 | ||
484 | 125 | func (msg *MessagingSuite) checkTags(c *C, got, expected []string) { | ||
485 | 126 | sort.Strings(got) | ||
486 | 127 | sort.Strings(expected) | ||
487 | 128 | c.Check(got, DeepEquals, expected) | ||
488 | 129 | } | ||
489 | 130 | |||
490 | 124 | func (ms *MessagingSuite) TestTagsListsTags(c *C) { | 131 | func (ms *MessagingSuite) TestTagsListsTags(c *C) { |
491 | 125 | mmu := New(ms.log) | 132 | mmu := New(ms.log) |
492 | 126 | f := func(s string) *launch_helper.Notification { | 133 | f := func(s string) *launch_helper.Notification { |
493 | @@ -130,15 +137,15 @@ | |||
494 | 130 | 137 | ||
495 | 131 | c.Check(mmu.Tags(ms.app), IsNil) | 138 | c.Check(mmu.Tags(ms.app), IsNil) |
496 | 132 | c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true) | 139 | c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true) |
498 | 133 | c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one"}) | 140 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one"}) |
499 | 134 | c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true) | 141 | c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true) |
501 | 135 | c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one", ""}) | 142 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
502 | 136 | // and an empty notification doesn't count | 143 | // and an empty notification doesn't count |
503 | 137 | c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false) | 144 | c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false) |
505 | 138 | c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one", ""}) | 145 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
506 | 139 | // and they go away if we remove one | 146 | // and they go away if we remove one |
507 | 140 | mmu.RemoveNotification("notif1", false) | 147 | mmu.RemoveNotification("notif1", false) |
509 | 141 | c.Check(mmu.Tags(ms.app), DeepEquals, []string{""}) | 148 | ms.checkTags(c, mmu.Tags(ms.app), []string{""}) |
510 | 142 | mmu.RemoveNotification("notif2", false) | 149 | mmu.RemoveNotification("notif2", false) |
511 | 143 | c.Check(mmu.Tags(ms.app), IsNil) | 150 | c.Check(mmu.Tags(ms.app), IsNil) |
512 | 144 | } | 151 | } |
513 | @@ -169,9 +176,9 @@ | |||
514 | 169 | // app 1: "one", "two", ""; | 176 | // app 1: "one", "two", ""; |
515 | 170 | // app 2: "one", "two"; | 177 | // app 2: "one", "two"; |
516 | 171 | // app 3: "one", "" | 178 | // app 3: "one", "" |
520 | 172 | c.Assert(mm.Tags(app1), DeepEquals, []string{"one", "two", ""}) | 179 | ms.checkTags(c, mm.Tags(app1), []string{"one", "two", ""}) |
521 | 173 | c.Assert(mm.Tags(app2), DeepEquals, []string{"one", "two"}) | 180 | ms.checkTags(c, mm.Tags(app2), []string{"one", "two"}) |
522 | 174 | c.Assert(mm.Tags(app3), DeepEquals, []string{"one", ""}) | 181 | ms.checkTags(c, mm.Tags(app3), []string{"one", ""}) |
523 | 175 | 182 | ||
524 | 176 | // clearing a non-existent tag does nothing | 183 | // clearing a non-existent tag does nothing |
525 | 177 | c.Check(mm.Clear(app1, "foo"), Equals, 0) | 184 | c.Check(mm.Clear(app1, "foo"), Equals, 0) |
526 | 178 | 185 | ||
527 | === modified file 'server/acceptance/suites/helpers.go' | |||
528 | --- server/acceptance/suites/helpers.go 2014-07-08 15:08:52 +0000 | |||
529 | +++ server/acceptance/suites/helpers.go 2014-11-25 18:40:27 +0000 | |||
530 | @@ -127,6 +127,11 @@ | |||
531 | 127 | c.Log(info) | 127 | c.Log(info) |
532 | 128 | continue | 128 | continue |
533 | 129 | } | 129 | } |
534 | 130 | if strings.HasPrefix(info, "DEBUG ") && !strings.HasPrefix(info, "DEBUG session(") { | ||
535 | 131 | // skip non session DEBUG logs | ||
536 | 132 | c.Log(info) | ||
537 | 133 | continue | ||
538 | 134 | } | ||
539 | 130 | logs <- info | 135 | logs <- info |
540 | 131 | } | 136 | } |
541 | 132 | }() | 137 | }() |
542 | @@ -136,16 +141,12 @@ | |||
543 | 136 | const ( | 141 | const ( |
544 | 137 | DevListeningOnPat = "INFO listening for devices on " | 142 | DevListeningOnPat = "INFO listening for devices on " |
545 | 138 | HTTPListeningOnPat = "INFO listening for http on " | 143 | HTTPListeningOnPat = "INFO listening for http on " |
546 | 139 | debugPrefix = "DEBUG " | ||
547 | 140 | ) | 144 | ) |
548 | 141 | 145 | ||
551 | 142 | // ExtractListeningAddr goes over logs ignoring DEBUG lines | 146 | // ExtractListeningAddr goes over logs until a line starting with pat |
552 | 143 | // until a line starting with pat and returns the rest of that line. | 147 | // and returns the rest of that line. |
553 | 144 | func ExtractListeningAddr(c *C, logs <-chan string, pat string) string { | 148 | func ExtractListeningAddr(c *C, logs <-chan string, pat string) string { |
554 | 145 | for line := range logs { | 149 | for line := range logs { |
555 | 146 | if strings.HasPrefix(line, debugPrefix) { | ||
556 | 147 | continue | ||
557 | 148 | } | ||
558 | 149 | if !strings.HasPrefix(line, pat) { | 150 | if !strings.HasPrefix(line, pat) { |
559 | 150 | c.Fatalf("matching %v: %v", pat, line) | 151 | c.Fatalf("matching %v: %v", pat, line) |
560 | 151 | } | 152 | } |
561 | 152 | 153 | ||
562 | === modified file 'server/api/handlers.go' | |||
563 | --- server/api/handlers.go 2014-07-14 15:23:17 +0000 | |||
564 | +++ server/api/handlers.go 2014-11-25 18:40:27 +0000 | |||
565 | @@ -23,6 +23,7 @@ | |||
566 | 23 | "encoding/json" | 23 | "encoding/json" |
567 | 24 | "fmt" | 24 | "fmt" |
568 | 25 | "io" | 25 | "io" |
569 | 26 | "mime" | ||
570 | 26 | "net/http" | 27 | "net/http" |
571 | 27 | "time" | 28 | "time" |
572 | 28 | 29 | ||
573 | @@ -98,6 +99,12 @@ | |||
574 | 98 | "Wrong request method, should be POST", | 99 | "Wrong request method, should be POST", |
575 | 99 | nil, | 100 | nil, |
576 | 100 | } | 101 | } |
577 | 102 | ErrWrongRequestMethodGET = &APIError{ | ||
578 | 103 | http.StatusMethodNotAllowed, | ||
579 | 104 | invalidRequest, | ||
580 | 105 | "Wrong request method, should be GET", | ||
581 | 106 | nil, | ||
582 | 107 | } | ||
583 | 101 | ErrMalformedJSONObject = &APIError{ | 108 | ErrMalformedJSONObject = &APIError{ |
584 | 102 | http.StatusBadRequest, | 109 | http.StatusBadRequest, |
585 | 103 | invalidRequest, | 110 | invalidRequest, |
586 | @@ -268,7 +275,8 @@ | |||
587 | 268 | if err := checkContentLength(request, maxBodySize); err != nil { | 275 | if err := checkContentLength(request, maxBodySize); err != nil { |
588 | 269 | return err | 276 | return err |
589 | 270 | } | 277 | } |
591 | 271 | if request.Header.Get("Content-Type") != JSONMediaType { | 278 | mediaType, _, err := mime.ParseMediaType(request.Header.Get("Content-Type")) |
592 | 279 | if err != nil || mediaType != JSONMediaType { | ||
593 | 272 | return ErrWrongContentType | 280 | return ErrWrongContentType |
594 | 273 | } | 281 | } |
595 | 274 | return nil | 282 | return nil |
596 | @@ -452,14 +460,17 @@ | |||
597 | 452 | if err != nil { | 460 | if err != nil { |
598 | 453 | switch err { | 461 | switch err { |
599 | 454 | case store.ErrUnknownToken: | 462 | case store.ErrUnknownToken: |
600 | 463 | ctx.logger.Debugf("notify: %v %v unknown", ucast.AppId, ucast.Token) | ||
601 | 455 | return nil, ErrUnknownToken | 464 | return nil, ErrUnknownToken |
602 | 456 | case store.ErrUnauthorized: | 465 | case store.ErrUnauthorized: |
603 | 466 | ctx.logger.Debugf("notify: %v %v unauthorized", ucast.AppId, ucast.Token) | ||
604 | 457 | return nil, ErrUnauthorized | 467 | return nil, ErrUnauthorized |
605 | 458 | default: | 468 | default: |
606 | 459 | ctx.logger.Errorf("could not resolve token: %v", err) | 469 | ctx.logger.Errorf("could not resolve token: %v", err) |
607 | 460 | return nil, ErrCouldNotResolveToken | 470 | return nil, ErrCouldNotResolveToken |
608 | 461 | } | 471 | } |
609 | 462 | } | 472 | } |
610 | 473 | ctx.logger.Debugf("notify: %v %v -> %v", ucast.AppId, ucast.Token, chanId) | ||
611 | 463 | 474 | ||
612 | 464 | _, notifs, meta, err := sto.GetChannelUnfiltered(chanId) | 475 | _, notifs, meta, err := sto.GetChannelUnfiltered(chanId) |
613 | 465 | if err != nil { | 476 | if err != nil { |
614 | @@ -491,6 +502,7 @@ | |||
615 | 491 | if ucast.ClearPending { | 502 | if ucast.ClearPending { |
616 | 492 | scrubCriteria = []string{ucast.AppId} | 503 | scrubCriteria = []string{ucast.AppId} |
617 | 493 | } else if forApp >= ctx.storage.GetMaxNotificationsPerApplication() { | 504 | } else if forApp >= ctx.storage.GetMaxNotificationsPerApplication() { |
618 | 505 | ctx.logger.Debugf("notify: %v %v too many pending", ucast.AppId, chanId) | ||
619 | 494 | return nil, apiErrorWithExtra(ErrTooManyPendingNotifications, | 506 | return nil, apiErrorWithExtra(ErrTooManyPendingNotifications, |
620 | 495 | &last.Payload) | 507 | &last.Payload) |
621 | 496 | } else if replaceable > 0 { | 508 | } else if replaceable > 0 { |
622 | @@ -518,6 +530,8 @@ | |||
623 | 518 | } | 530 | } |
624 | 519 | 531 | ||
625 | 520 | ctx.broker.Unicast(chanId) | 532 | ctx.broker.Unicast(chanId) |
626 | 533 | |||
627 | 534 | ctx.logger.Debugf("notify: ok %v %v id:%v clear:%v replace:%v expired:%v", ucast.AppId, chanId, msgId, ucast.ClearPending, replaceable, expired) | ||
628 | 521 | return nil, nil | 535 | return nil, nil |
629 | 522 | } | 536 | } |
630 | 523 | 537 | ||
631 | 524 | 538 | ||
632 | === modified file 'server/api/handlers_test.go' | |||
633 | --- server/api/handlers_test.go 2014-07-15 15:03:49 +0000 | |||
634 | +++ server/api/handlers_test.go 2014-11-25 18:40:27 +0000 | |||
635 | @@ -362,7 +362,7 @@ | |||
636 | 362 | } | 362 | } |
637 | 363 | sto := store.NewInMemoryPendingStore() | 363 | sto := store.NewInMemoryPendingStore() |
638 | 364 | bsend := &checkBrokerSending{store: sto} | 364 | bsend := &checkBrokerSending{store: sto} |
640 | 365 | ctx := &context{testStoreAccess(nil), bsend, nil} | 365 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
641 | 366 | payload := json.RawMessage(`{"a": 1}`) | 366 | payload := json.RawMessage(`{"a": 1}`) |
642 | 367 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 367 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
643 | 368 | UserId: "user1", | 368 | UserId: "user1", |
644 | @@ -487,7 +487,7 @@ | |||
645 | 487 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) | 487 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
646 | 488 | 488 | ||
647 | 489 | bsend := &checkBrokerSending{store: sto} | 489 | bsend := &checkBrokerSending{store: sto} |
649 | 490 | ctx := &context{testStoreAccess(nil), bsend, nil} | 490 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
650 | 491 | payload := json.RawMessage(`{"a": 1}`) | 491 | payload := json.RawMessage(`{"a": 1}`) |
651 | 492 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 492 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
652 | 493 | UserId: "user1", | 493 | UserId: "user1", |
653 | @@ -528,7 +528,7 @@ | |||
654 | 528 | sto.AppendToUnicastChannel(chanId, "app1", n, "m1", expire) | 528 | sto.AppendToUnicastChannel(chanId, "app1", n, "m1", expire) |
655 | 529 | 529 | ||
656 | 530 | bsend := &checkBrokerSending{store: sto} | 530 | bsend := &checkBrokerSending{store: sto} |
658 | 531 | ctx := &context{testStoreAccess(nil), bsend, nil} | 531 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
659 | 532 | payload := json.RawMessage(`{"a": 1}`) | 532 | payload := json.RawMessage(`{"a": 1}`) |
660 | 533 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 533 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
661 | 534 | UserId: "user1", | 534 | UserId: "user1", |
662 | @@ -634,7 +634,7 @@ | |||
663 | 634 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) | 634 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
664 | 635 | 635 | ||
665 | 636 | bsend := &checkBrokerSending{store: sto} | 636 | bsend := &checkBrokerSending{store: sto} |
667 | 637 | ctx := &context{testStoreAccess(nil), bsend, nil} | 637 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
668 | 638 | payload := json.RawMessage(`{"a": 1}`) | 638 | payload := json.RawMessage(`{"a": 1}`) |
669 | 639 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 639 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
670 | 640 | UserId: "user1", | 640 | UserId: "user1", |
671 | @@ -933,6 +933,22 @@ | |||
672 | 933 | checkError(c, response, ErrWrongContentType) | 933 | checkError(c, response, ErrWrongContentType) |
673 | 934 | } | 934 | } |
674 | 935 | 935 | ||
675 | 936 | func (s *handlersSuite) TestContentTypeWithCharset(c *C) { | ||
676 | 937 | testServer := httptest.NewServer(&JSONPostHandler{}) | ||
677 | 938 | defer testServer.Close() | ||
678 | 939 | |||
679 | 940 | dataString := `{"foo":"bar"}` | ||
680 | 941 | |||
681 | 942 | request := newPostRequest("/", &Broadcast{ | ||
682 | 943 | Channel: "some-channel", | ||
683 | 944 | ExpireOn: future, | ||
684 | 945 | Data: json.RawMessage([]byte(dataString)), | ||
685 | 946 | }, testServer) | ||
686 | 947 | request.Header.Set("Content-Type", "application/json; charset=UTF-8") | ||
687 | 948 | result := checkRequestAsPost(request, 1024) | ||
688 | 949 | c.Assert(result, IsNil) | ||
689 | 950 | } | ||
690 | 951 | |||
691 | 936 | func (s *handlersSuite) TestCannotBroadcastNonPostMessages(c *C) { | 952 | func (s *handlersSuite) TestCannotBroadcastNonPostMessages(c *C) { |
692 | 937 | testServer := httptest.NewServer(&JSONPostHandler{}) | 953 | testServer := httptest.NewServer(&JSONPostHandler{}) |
693 | 938 | defer testServer.Close() | 954 | defer testServer.Close() |
694 | @@ -965,7 +981,7 @@ | |||
695 | 965 | return sto, nil | 981 | return sto, nil |
696 | 966 | }) | 982 | }) |
697 | 967 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} | 983 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
699 | 968 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) | 984 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
700 | 969 | defer testServer.Close() | 985 | defer testServer.Close() |
701 | 970 | 986 | ||
702 | 971 | payload := json.RawMessage(`{"foo":"bar"}`) | 987 | payload := json.RawMessage(`{"foo":"bar"}`) |
703 | @@ -1049,7 +1065,7 @@ | |||
704 | 1049 | return sto, nil | 1065 | return sto, nil |
705 | 1050 | }) | 1066 | }) |
706 | 1051 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} | 1067 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
708 | 1052 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) | 1068 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
709 | 1053 | defer testServer.Close() | 1069 | defer testServer.Close() |
710 | 1054 | 1070 | ||
711 | 1055 | request := newPostRequest("/register", &Registration{ | 1071 | request := newPostRequest("/register", &Registration{ |
712 | 1056 | 1072 | ||
713 | === modified file 'server/broker/testsuite/suite.go' | |||
714 | --- server/broker/testsuite/suite.go 2014-09-25 11:16:38 +0000 | |||
715 | +++ server/broker/testsuite/suite.go 2014-11-25 18:40:27 +0000 | |||
716 | @@ -66,12 +66,12 @@ | |||
717 | 66 | 66 | ||
718 | 67 | func (s *CommonBrokerSuite) TestSanity(c *C) { | 67 | func (s *CommonBrokerSuite) TestSanity(c *C) { |
719 | 68 | sto := store.NewInMemoryPendingStore() | 68 | sto := store.NewInMemoryPendingStore() |
721 | 69 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 69 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
722 | 70 | c.Check(s.RevealSession(b, "FOO"), IsNil) | 70 | c.Check(s.RevealSession(b, "FOO"), IsNil) |
723 | 71 | } | 71 | } |
724 | 72 | 72 | ||
725 | 73 | func (s *CommonBrokerSuite) TestStartStop(c *C) { | 73 | func (s *CommonBrokerSuite) TestStartStop(c *C) { |
727 | 74 | b := s.MakeBroker(nil, testBrokerConfig, nil) | 74 | b := s.MakeBroker(nil, testBrokerConfig, s.testlog) |
728 | 75 | b.Start() | 75 | b.Start() |
729 | 76 | c.Check(b.Running(), Equals, true) | 76 | c.Check(b.Running(), Equals, true) |
730 | 77 | b.Start() | 77 | b.Start() |
731 | @@ -82,7 +82,7 @@ | |||
732 | 82 | 82 | ||
733 | 83 | func (s *CommonBrokerSuite) TestRegistration(c *C) { | 83 | func (s *CommonBrokerSuite) TestRegistration(c *C) { |
734 | 84 | sto := store.NewInMemoryPendingStore() | 84 | sto := store.NewInMemoryPendingStore() |
736 | 85 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 85 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
737 | 86 | b.Start() | 86 | b.Start() |
738 | 87 | defer b.Stop() | 87 | defer b.Stop() |
739 | 88 | sess, err := b.Register(&protocol.ConnectMsg{ | 88 | sess, err := b.Register(&protocol.ConnectMsg{ |
740 | @@ -112,7 +112,7 @@ | |||
741 | 112 | 112 | ||
742 | 113 | func (s *CommonBrokerSuite) TestRegistrationBrokenLevels(c *C) { | 113 | func (s *CommonBrokerSuite) TestRegistrationBrokenLevels(c *C) { |
743 | 114 | sto := store.NewInMemoryPendingStore() | 114 | sto := store.NewInMemoryPendingStore() |
745 | 115 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 115 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
746 | 116 | b.Start() | 116 | b.Start() |
747 | 117 | defer b.Stop() | 117 | defer b.Stop() |
748 | 118 | _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}, s.MakeTracker("s1")) | 118 | _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}, s.MakeTracker("s1")) |
749 | @@ -121,7 +121,7 @@ | |||
750 | 121 | 121 | ||
751 | 122 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { | 122 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { |
752 | 123 | sto := store.NewInMemoryPendingStore() | 123 | sto := store.NewInMemoryPendingStore() |
754 | 124 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 124 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
755 | 125 | b.Start() | 125 | b.Start() |
756 | 126 | defer b.Stop() | 126 | defer b.Stop() |
757 | 127 | info := map[string]interface{}{ | 127 | info := map[string]interface{}{ |
758 | @@ -140,7 +140,7 @@ | |||
759 | 140 | notification1 := json.RawMessage(`{"m": "M"}`) | 140 | notification1 := json.RawMessage(`{"m": "M"}`) |
760 | 141 | muchLater := time.Now().Add(10 * time.Minute) | 141 | muchLater := time.Now().Add(10 * time.Minute) |
761 | 142 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) | 142 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) |
763 | 143 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 143 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
764 | 144 | b.Start() | 144 | b.Start() |
765 | 145 | defer b.Stop() | 145 | defer b.Stop() |
766 | 146 | sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) | 146 | sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
767 | @@ -166,7 +166,7 @@ | |||
768 | 166 | 166 | ||
769 | 167 | func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) { | 167 | func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) { |
770 | 168 | sto := store.NewInMemoryPendingStore() | 168 | sto := store.NewInMemoryPendingStore() |
772 | 169 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 169 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
773 | 170 | b.Start() | 170 | b.Start() |
774 | 171 | defer b.Stop() | 171 | defer b.Stop() |
775 | 172 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) | 172 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
776 | @@ -197,7 +197,7 @@ | |||
777 | 197 | sto := store.NewInMemoryPendingStore() | 197 | sto := store.NewInMemoryPendingStore() |
778 | 198 | notification1 := json.RawMessage(`{"m": "M"}`) | 198 | notification1 := json.RawMessage(`{"m": "M"}`) |
779 | 199 | decoded1 := map[string]interface{}{"m": "M"} | 199 | decoded1 := map[string]interface{}{"m": "M"} |
781 | 200 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 200 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
782 | 201 | b.Start() | 201 | b.Start() |
783 | 202 | defer b.Stop() | 202 | defer b.Stop() |
784 | 203 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) | 203 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
785 | @@ -278,7 +278,7 @@ | |||
786 | 278 | notification2 := json.RawMessage(`{"m": "M2"}`) | 278 | notification2 := json.RawMessage(`{"m": "M2"}`) |
787 | 279 | chanId1 := store.UnicastInternalChannelId("dev1", "dev1") | 279 | chanId1 := store.UnicastInternalChannelId("dev1", "dev1") |
788 | 280 | chanId2 := store.UnicastInternalChannelId("dev2", "dev2") | 280 | chanId2 := store.UnicastInternalChannelId("dev2", "dev2") |
790 | 281 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 281 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
791 | 282 | b.Start() | 282 | b.Start() |
792 | 283 | defer b.Stop() | 283 | defer b.Stop() |
793 | 284 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev1"}, s.MakeTracker("s1")) | 284 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev1"}, s.MakeTracker("s1")) |
794 | @@ -312,7 +312,7 @@ | |||
795 | 312 | sto := store.NewInMemoryPendingStore() | 312 | sto := store.NewInMemoryPendingStore() |
796 | 313 | notification1 := json.RawMessage(`{"m": "M1"}`) | 313 | notification1 := json.RawMessage(`{"m": "M1"}`) |
797 | 314 | chanId1 := store.UnicastInternalChannelId("dev3", "dev3") | 314 | chanId1 := store.UnicastInternalChannelId("dev3", "dev3") |
799 | 315 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 315 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
800 | 316 | b.Start() | 316 | b.Start() |
801 | 317 | defer b.Stop() | 317 | defer b.Stop() |
802 | 318 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) | 318 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) |
803 | @@ -354,7 +354,7 @@ | |||
804 | 354 | 354 | ||
805 | 355 | func (s *CommonBrokerSuite) TestSessionFeed(c *C) { | 355 | func (s *CommonBrokerSuite) TestSessionFeed(c *C) { |
806 | 356 | sto := store.NewInMemoryPendingStore() | 356 | sto := store.NewInMemoryPendingStore() |
808 | 357 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 357 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
809 | 358 | b.Start() | 358 | b.Start() |
810 | 359 | defer b.Stop() | 359 | defer b.Stop() |
811 | 360 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) | 360 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) |
812 | 361 | 361 | ||
813 | === modified file 'server/listener/listener_test.go' | |||
814 | --- server/listener/listener_test.go 2014-09-25 11:20:00 +0000 | |||
815 | +++ server/listener/listener_test.go 2014-11-25 18:40:27 +0000 | |||
816 | @@ -18,6 +18,7 @@ | |||
817 | 18 | 18 | ||
818 | 19 | import ( | 19 | import ( |
819 | 20 | "crypto/tls" | 20 | "crypto/tls" |
820 | 21 | "io" | ||
821 | 21 | "net" | 22 | "net" |
822 | 22 | "os/exec" | 23 | "os/exec" |
823 | 23 | "regexp" | 24 | "regexp" |
824 | @@ -112,7 +113,7 @@ | |||
825 | 112 | conn.SetDeadline(time.Now().Add(10 * time.Second)) | 113 | conn.SetDeadline(time.Now().Add(10 * time.Second)) |
826 | 113 | var buf [1]byte | 114 | var buf [1]byte |
827 | 114 | for { | 115 | for { |
829 | 115 | _, err := conn.Read(buf[:]) | 116 | _, err := io.ReadFull(conn, buf[:]) |
830 | 116 | if err != nil { | 117 | if err != nil { |
831 | 117 | return err | 118 | return err |
832 | 118 | } | 119 | } |
833 | @@ -137,7 +138,7 @@ | |||
834 | 137 | 138 | ||
835 | 138 | func testReadByte(c *C, conn net.Conn, expected uint32) { | 139 | func testReadByte(c *C, conn net.Conn, expected uint32) { |
836 | 139 | var buf [1]byte | 140 | var buf [1]byte |
838 | 140 | _, err := conn.Read(buf[:]) | 141 | _, err := io.ReadFull(conn, buf[:]) |
839 | 141 | c.Check(err, IsNil) | 142 | c.Check(err, IsNil) |
840 | 142 | c.Check(buf[0], Equals, byte(expected)) | 143 | c.Check(buf[0], Equals, byte(expected)) |
841 | 143 | } | 144 | } |