Merge lp:~ralsina/ubuntu-push/server-limits-doc into lp:ubuntu-push
- server-limits-doc
- Merge into trunk
Proposed by
Roberto Alsina
Status: | Superseded |
---|---|
Proposed branch: | lp:~ralsina/ubuntu-push/server-limits-doc |
Merge into: | lp:ubuntu-push |
Diff against target: |
554 lines (+146/-51) 15 files modified
PACKAGE_DEPS (+1/-0) client/service/service.go (+2/-0) client/service/service_test.go (+17/-0) client/session/session.go (+3/-3) 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) server/acceptance/suites/helpers.go (+7/-6) server/api/handlers.go (+12/-0) server/api/handlers_test.go (+6/-6) server/broker/testsuite/suite.go (+11/-11) |
To merge this branch: | bzr merge lp:~ralsina/ubuntu-push/server-limits-doc |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2014-11-17.
Commit message
Add section describing limitations of the server API
Description of the change
Add section describing limitations of the server API
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 'PACKAGE_DEPS' | |||
2 | --- PACKAGE_DEPS 2014-09-05 10:48:36 +0000 | |||
3 | +++ PACKAGE_DEPS 2014-11-13 14:37:31 +0000 | |||
4 | @@ -12,3 +12,4 @@ | |||
5 | 12 | libclick-0.4-dev | 12 | libclick-0.4-dev |
6 | 13 | liburl-dispatcher1-dev | 13 | liburl-dispatcher1-dev |
7 | 14 | libaccounts-glib-dev | 14 | libaccounts-glib-dev |
8 | 15 | system-image-dbus | ||
9 | 15 | 16 | ||
10 | === modified file 'client/service/service.go' | |||
11 | --- client/service/service.go 2014-11-03 13:36:00 +0000 | |||
12 | +++ client/service/service.go 2014-11-13 14:37:31 +0000 | |||
13 | @@ -140,6 +140,8 @@ | |||
14 | 140 | case resp.StatusCode >= http.StatusInternalServerError: | 140 | case resp.StatusCode >= http.StatusInternalServerError: |
15 | 141 | // XXX retry on 503 | 141 | // XXX retry on 503 |
16 | 142 | return nil, ErrBadServer | 142 | return nil, ErrBadServer |
17 | 143 | case resp.StatusCode == http.StatusUnauthorized: | ||
18 | 144 | return nil, ErrBadAuth | ||
19 | 143 | default: | 145 | default: |
20 | 144 | return nil, ErrBadRequest | 146 | return nil, ErrBadRequest |
21 | 145 | } | 147 | } |
22 | 146 | 148 | ||
23 | === modified file 'client/service/service_test.go' | |||
24 | --- client/service/service_test.go 2014-08-06 09:01:59 +0000 | |||
25 | +++ client/service/service_test.go 2014-11-13 14:37:31 +0000 | |||
26 | @@ -240,6 +240,23 @@ | |||
27 | 240 | c.Check(err, ErrorMatches, "unable to request registration: .*") | 240 | c.Check(err, ErrorMatches, "unable to request registration: .*") |
28 | 241 | } | 241 | } |
29 | 242 | 242 | ||
30 | 243 | func (ss *serviceSuite) TestManageRegFailsOn401(c *C) { | ||
31 | 244 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
32 | 245 | http.Error(w, "Unauthorized", 401) | ||
33 | 246 | })) | ||
34 | 247 | defer ts.Close() | ||
35 | 248 | setup := &PushServiceSetup{ | ||
36 | 249 | DeviceId: "fake-device-id", | ||
37 | 250 | RegURL: helpers.ParseURL(ts.URL), | ||
38 | 251 | AuthGetter: func(string) string { return "tok" }, | ||
39 | 252 | } | ||
40 | 253 | svc := NewPushService(setup, ss.log) | ||
41 | 254 | svc.Bus = ss.bus | ||
42 | 255 | reg, err := svc.register(aPackageOnBus, []interface{}{anAppId}, nil) | ||
43 | 256 | c.Check(err, Equals, ErrBadAuth) | ||
44 | 257 | c.Check(reg, IsNil) | ||
45 | 258 | } | ||
46 | 259 | |||
47 | 243 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { | 260 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { |
48 | 244 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 261 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
49 | 245 | http.Error(w, "I'm a teapot", 418) | 262 | http.Error(w, "I'm a teapot", 418) |
50 | 246 | 263 | ||
51 | === modified file 'client/session/session.go' | |||
52 | --- client/session/session.go 2014-11-03 13:36:00 +0000 | |||
53 | +++ client/session/session.go 2014-11-13 14:37:31 +0000 | |||
54 | @@ -430,7 +430,7 @@ | |||
55 | 430 | return err | 430 | return err |
56 | 431 | } | 431 | } |
57 | 432 | sess.clearShouldDelay() | 432 | sess.clearShouldDelay() |
59 | 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", |
60 | 434 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) | 434 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) |
61 | 435 | if bcast.ChanId == protocol.SystemChannelId { | 435 | if bcast.ChanId == protocol.SystemChannelId { |
62 | 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 |
63 | @@ -438,7 +438,7 @@ | |||
64 | 438 | sess.BroadcastCh <- sess.decodeBroadcast(bcast) | 438 | sess.BroadcastCh <- sess.decodeBroadcast(bcast) |
65 | 439 | sess.Log.Debugf("sent bcast over") | 439 | sess.Log.Debugf("sent bcast over") |
66 | 440 | } else { | 440 | } else { |
68 | 441 | sess.Log.Debugf("what is this weird channel, %#v?", bcast.ChanId) | 441 | sess.Log.Errorf("what is this weird channel, %#v?", bcast.ChanId) |
69 | 442 | } | 442 | } |
70 | 443 | return nil | 443 | return nil |
71 | 444 | } | 444 | } |
72 | @@ -468,7 +468,7 @@ | |||
73 | 468 | if to == nil { | 468 | if to == nil { |
74 | 469 | continue | 469 | continue |
75 | 470 | } | 470 | } |
77 | 471 | sess.Log.Debugf("unicast app:%v msg:%s payload:%s", | 471 | sess.Log.Infof("unicast app:%v msg:%s payload:%s", |
78 | 472 | notif.AppId, notif.MsgId, notif.Payload) | 472 | notif.AppId, notif.MsgId, notif.Payload) |
79 | 473 | sess.Log.Debugf("sending ucast over") | 473 | sess.Log.Debugf("sending ucast over") |
80 | 474 | sess.NotificationsCh <- AddressedNotification{to, notif} | 474 | sess.NotificationsCh <- AddressedNotification{to, notif} |
81 | 475 | 475 | ||
82 | === modified file 'debian/config.json' | |||
83 | --- debian/config.json 2014-08-21 10:47:12 +0000 | |||
84 | +++ debian/config.json 2014-11-13 14:37:31 +0000 | |||
85 | @@ -12,7 +12,7 @@ | |||
86 | 12 | "recheck_timeout": "10m", | 12 | "recheck_timeout": "10m", |
87 | 13 | "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html", | 13 | "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html", |
88 | 14 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", | 14 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", |
90 | 15 | "log_level": "debug", | 15 | "log_level": "info", |
91 | 16 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, | 16 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, |
92 | 17 | "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", | 17 | "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", |
93 | 18 | "poll_interval": "5m", | 18 | "poll_interval": "5m", |
94 | 19 | 19 | ||
95 | === modified file 'docs/_common.txt' | |||
96 | --- docs/_common.txt 2014-10-09 16:06:52 +0000 | |||
97 | +++ docs/_common.txt 2014-11-13 14:37:31 +0000 | |||
98 | @@ -185,3 +185,46 @@ | |||
99 | 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. |
100 | 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. |
101 | 187 | :data: A JSON object. | 187 | :data: A JSON object. |
102 | 188 | |||
103 | 189 | Limitations of the Server API | ||
104 | 190 | ----------------------------- | ||
105 | 191 | |||
106 | 192 | The push notification infrastructure is meant to help ensuring timely | ||
107 | 193 | delivery of application notifications if the device is online or | ||
108 | 194 | timely informing the device user about application notifications that | ||
109 | 195 | were pending when the device comes back online. This in the face of | ||
110 | 196 | applications not being allowed to be running all the time, and | ||
111 | 197 | avoiding the resource cost of many applications all polling different services | ||
112 | 198 | frequently. | ||
113 | 199 | |||
114 | 200 | The push notification infrastructure is architected to guarantee at | ||
115 | 201 | least best-effort with respect to these goals and beyond it, on the | ||
116 | 202 | other end applications should not expect to be able to use and only | ||
117 | 203 | rely on the push notification infrastructure to store application | ||
118 | 204 | messages if they want ensure all their notification or messages are | ||
119 | 205 | delivered, the infrastructure is not intended to be the only long term | ||
120 | 206 | "inbox" storage for an application. | ||
121 | 207 | |||
122 | 208 | To preserve overall throughput the infrastructure imposes some limits | ||
123 | 209 | on applications: | ||
124 | 210 | |||
125 | 211 | * message data payload is limited to 2K | ||
126 | 212 | |||
127 | 213 | * when inserted all messages need to specify an expiration date after | ||
128 | 214 | which they can be dropped and not delivered | ||
129 | 215 | |||
130 | 216 | * an application is limited in the number of messages per token | ||
131 | 217 | (application/user/device combination) that can be undelivered/pending at the | ||
132 | 218 | same time (100 currently) | ||
133 | 219 | |||
134 | 220 | replace_tag can be used to implement notifications for which the newest | ||
135 | 221 | one replace the previous one if pending. | ||
136 | 222 | |||
137 | 223 | clear_pending can be used to be deal with a pending message limit | ||
138 | 224 | reached, possibly substituting the current undelivered messages with a | ||
139 | 225 | more generic one. | ||
140 | 226 | |||
141 | 227 | Applications using the push notification HTTP API should be robust | ||
142 | 228 | against receiving 503 errors, retrying after waiting with increasing | ||
143 | 229 | back-off. Later rate limits (signaled with the 429 status) may also come | ||
144 | 230 | into play. | ||
145 | 188 | 231 | ||
146 | === modified file 'docs/example-client/main.qml' | |||
147 | --- docs/example-client/main.qml 2014-09-10 14:38:40 +0000 | |||
148 | +++ docs/example-client/main.qml 2014-11-13 14:37:31 +0000 | |||
149 | @@ -26,9 +26,42 @@ | |||
150 | 26 | property alias nickEnabled: nickEdit.enabled | 26 | property alias nickEnabled: nickEdit.enabled |
151 | 27 | } | 27 | } |
152 | 28 | 28 | ||
153 | 29 | states: [ | ||
154 | 30 | State { | ||
155 | 31 | name: "no-push-token" | ||
156 | 32 | when: (pushClient.token == "") | ||
157 | 33 | PropertyChanges { target: nickEdit; readOnly: true} | ||
158 | 34 | PropertyChanges { target: nickEdit; focus: true} | ||
159 | 35 | PropertyChanges { target: messageEdit; enabled: false} | ||
160 | 36 | PropertyChanges { target: loginButton; enabled: false} | ||
161 | 37 | PropertyChanges { target: loginButton; text: "Login"} | ||
162 | 38 | }, | ||
163 | 39 | State { | ||
164 | 40 | name: "push-token-not-registered" | ||
165 | 41 | when: ((pushClient.token != "") && (chatClient.registered == false)) | ||
166 | 42 | PropertyChanges { target: nickEdit; readOnly: false} | ||
167 | 43 | PropertyChanges { target: nickEdit; text: ""} | ||
168 | 44 | PropertyChanges { target: nickEdit; focus: true} | ||
169 | 45 | PropertyChanges { target: messageEdit; enabled: false} | ||
170 | 46 | PropertyChanges { target: loginButton; enabled: true} | ||
171 | 47 | PropertyChanges { target: loginButton; text: "Login"} | ||
172 | 48 | }, | ||
173 | 49 | State { | ||
174 | 50 | name: "registered" | ||
175 | 51 | when: ((pushClient.token != "") && (chatClient.registered == true)) | ||
176 | 52 | PropertyChanges { target: nickEdit; readOnly: true} | ||
177 | 53 | PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick} | ||
178 | 54 | PropertyChanges { target: messageEdit; focus: true} | ||
179 | 55 | PropertyChanges { target: messageEdit; enabled: true} | ||
180 | 56 | PropertyChanges { target: loginButton; enabled: true} | ||
181 | 57 | PropertyChanges { target: loginButton; text: "Logout"} | ||
182 | 58 | } | ||
183 | 59 | ] | ||
184 | 60 | |||
185 | 61 | state: "no-push-token" | ||
186 | 62 | |||
187 | 29 | ChatClient { | 63 | ChatClient { |
188 | 30 | id: chatClient | 64 | id: chatClient |
189 | 31 | onRegisteredChanged: {nickEdit.registered()} | ||
190 | 32 | onError: {messageList.handle_error(msg)} | 65 | onError: {messageList.handle_error(msg)} |
191 | 33 | token: pushClient.token | 66 | token: pushClient.token |
192 | 34 | } | 67 | } |
193 | @@ -44,7 +77,6 @@ | |||
194 | 44 | 77 | ||
195 | 45 | TextField { | 78 | TextField { |
196 | 46 | id: nickEdit | 79 | id: nickEdit |
197 | 47 | focus: true | ||
198 | 48 | placeholderText: "Your nickname" | 80 | placeholderText: "Your nickname" |
199 | 49 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase | 81 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase |
200 | 50 | anchors.left: parent.left | 82 | anchors.left: parent.left |
201 | @@ -53,31 +85,17 @@ | |||
202 | 53 | anchors.leftMargin: units.gu(.5) | 85 | anchors.leftMargin: units.gu(.5) |
203 | 54 | anchors.rightMargin: units.gu(1) | 86 | anchors.rightMargin: units.gu(1) |
204 | 55 | anchors.topMargin: units.gu(.5) | 87 | anchors.topMargin: units.gu(.5) |
205 | 56 | function registered() { | ||
206 | 57 | readOnly = true | ||
207 | 58 | text = "Your nick is " + chatClient.nick | ||
208 | 59 | messageEdit.focus = true | ||
209 | 60 | messageEdit.enabled = true | ||
210 | 61 | loginButton.text = "Logout" | ||
211 | 62 | } | ||
212 | 63 | onAccepted: { loginButton.clicked() } | 88 | onAccepted: { loginButton.clicked() } |
213 | 64 | } | 89 | } |
214 | 65 | 90 | ||
215 | 66 | Button { | 91 | Button { |
216 | 67 | id: loginButton | 92 | id: loginButton |
217 | 68 | text: chatClient.rgistered? "Logout": "Login" | ||
218 | 69 | anchors.top: nickEdit.top | 93 | anchors.top: nickEdit.top |
219 | 70 | anchors.right: parent.right | 94 | anchors.right: parent.right |
220 | 71 | anchors.rightMargin: units.gu(.5) | 95 | anchors.rightMargin: units.gu(.5) |
221 | 72 | onClicked: { | 96 | onClicked: { |
222 | 73 | if (chatClient.nick) { // logout | 97 | if (chatClient.nick) { // logout |
223 | 74 | chatClient.nick = "" | 98 | chatClient.nick = "" |
224 | 75 | text = "Login" | ||
225 | 76 | nickEdit.enabled = true | ||
226 | 77 | nickEdit.readOnly = false | ||
227 | 78 | nickEdit.text = "" | ||
228 | 79 | nickEdit.focus = true | ||
229 | 80 | messageEdit.enabled = false | ||
230 | 81 | } else { // login | 99 | } else { // login |
231 | 82 | chatClient.nick = nickEdit.text | 100 | chatClient.nick = nickEdit.text |
232 | 83 | } | 101 | } |
233 | @@ -94,7 +112,6 @@ | |||
234 | 94 | anchors.rightMargin: units.gu(.5) | 112 | anchors.rightMargin: units.gu(.5) |
235 | 95 | anchors.leftMargin: units.gu(.5) | 113 | anchors.leftMargin: units.gu(.5) |
236 | 96 | placeholderText: "Your message" | 114 | placeholderText: "Your message" |
237 | 97 | enabled: false | ||
238 | 98 | onAccepted: { | 115 | onAccepted: { |
239 | 99 | console.log("sending " + text) | 116 | console.log("sending " + text) |
240 | 100 | var idx = text.indexOf(":") | 117 | var idx = text.indexOf(":") |
241 | 101 | 118 | ||
242 | === modified file 'docs/example-client/manifest.json' | |||
243 | --- docs/example-client/manifest.json 2014-09-10 14:38:31 +0000 | |||
244 | +++ docs/example-client/manifest.json 2014-11-13 14:37:31 +0000 | |||
245 | @@ -15,5 +15,5 @@ | |||
246 | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", |
247 | 16 | "name": "com.ubuntu.developer.ralsina.hello", | 16 | "name": "com.ubuntu.developer.ralsina.hello", |
248 | 17 | "title": "Hello", | 17 | "title": "Hello", |
250 | 18 | "version": "0.4.2" | 18 | "version": "0.4.3" |
251 | 19 | } | 19 | } |
252 | 20 | 20 | ||
253 | === modified file 'docs/lowlevel.txt' | |||
254 | --- docs/lowlevel.txt 2014-10-15 16:34:25 +0000 | |||
255 | +++ docs/lowlevel.txt 2014-11-13 14:37:31 +0000 | |||
256 | @@ -57,6 +57,8 @@ | |||
257 | 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 |
258 | 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. |
259 | 59 | 59 | ||
260 | 60 | In the case the Register method returns a "bad auth" error, the application should inform the user to generate new Ubuntu One tokens. | ||
261 | 61 | |||
262 | 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>`__ |
263 | 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`` |
264 | 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 |
265 | 63 | 65 | ||
266 | === modified file 'launch_helper/kindpool.go' | |||
267 | --- launch_helper/kindpool.go 2014-08-20 12:42:51 +0000 | |||
268 | +++ launch_helper/kindpool.go 2014-11-13 14:37:31 +0000 | |||
269 | @@ -294,12 +294,13 @@ | |||
270 | 294 | } | 294 | } |
271 | 295 | payload, err := ioutil.ReadFile(args.FileOut) | 295 | payload, err := ioutil.ReadFile(args.FileOut) |
272 | 296 | if err != nil { | 296 | if err != nil { |
274 | 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) |
275 | 298 | } else { | 298 | } else { |
276 | 299 | pool.log.Infof("%v helper output: %#v", args.AppId, payload) | ||
277 | 299 | res := &HelperResult{Input: args.Input} | 300 | res := &HelperResult{Input: args.Input} |
278 | 300 | err = json.Unmarshal(payload, &res.HelperOutput) | 301 | err = json.Unmarshal(payload, &res.HelperOutput) |
279 | 301 | if err != nil { | 302 | if err != nil { |
281 | 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) |
282 | 303 | } else { | 304 | } else { |
283 | 304 | pool.chOut <- res | 305 | pool.chOut <- res |
284 | 305 | } | 306 | } |
285 | 306 | 307 | ||
286 | === modified file 'launch_helper/legacy/legacy.go' | |||
287 | --- launch_helper/legacy/legacy.go 2014-08-21 18:03:49 +0000 | |||
288 | +++ launch_helper/legacy/legacy.go 2014-11-13 14:37:31 +0000 | |||
289 | @@ -55,7 +55,7 @@ | |||
290 | 55 | err error | 55 | err error |
291 | 56 | } | 56 | } |
292 | 57 | 57 | ||
294 | 58 | func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) { | 58 | func (lhl *legacyHelperLauncher) Launch(appId, progname, f1, f2 string) (string, error) { |
295 | 59 | comm := make(chan msg) | 59 | comm := make(chan msg) |
296 | 60 | 60 | ||
297 | 61 | go func() { | 61 | go func() { |
298 | @@ -78,9 +78,8 @@ | |||
299 | 78 | p_err := cmd.Wait() | 78 | p_err := cmd.Wait() |
300 | 79 | if p_err != nil { | 79 | if p_err != nil { |
301 | 80 | // Helper failed or got killed, log output/errors | 80 | // Helper failed or got killed, log output/errors |
305 | 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.", |
306 | 82 | lhl.log.Errorf("Legacy helper failed. Stdout: %s", stdout) | 82 | appId, progname, id, p_err, stdout.String(), stderr.String()) |
304 | 83 | lhl.log.Errorf("Legacy helper failed. Stderr: %s", stderr) | ||
307 | 84 | } | 83 | } |
308 | 85 | lhl.done(id) | 84 | lhl.done(id) |
309 | 86 | }() | 85 | }() |
310 | 87 | 86 | ||
311 | === modified file 'server/acceptance/suites/helpers.go' | |||
312 | --- server/acceptance/suites/helpers.go 2014-07-08 15:08:52 +0000 | |||
313 | +++ server/acceptance/suites/helpers.go 2014-11-13 14:37:31 +0000 | |||
314 | @@ -127,6 +127,11 @@ | |||
315 | 127 | c.Log(info) | 127 | c.Log(info) |
316 | 128 | continue | 128 | continue |
317 | 129 | } | 129 | } |
318 | 130 | if strings.HasPrefix(info, "DEBUG ") && !strings.HasPrefix(info, "DEBUG session(") { | ||
319 | 131 | // skip non session DEBUG logs | ||
320 | 132 | c.Log(info) | ||
321 | 133 | continue | ||
322 | 134 | } | ||
323 | 130 | logs <- info | 135 | logs <- info |
324 | 131 | } | 136 | } |
325 | 132 | }() | 137 | }() |
326 | @@ -136,16 +141,12 @@ | |||
327 | 136 | const ( | 141 | const ( |
328 | 137 | DevListeningOnPat = "INFO listening for devices on " | 142 | DevListeningOnPat = "INFO listening for devices on " |
329 | 138 | HTTPListeningOnPat = "INFO listening for http on " | 143 | HTTPListeningOnPat = "INFO listening for http on " |
330 | 139 | debugPrefix = "DEBUG " | ||
331 | 140 | ) | 144 | ) |
332 | 141 | 145 | ||
335 | 142 | // ExtractListeningAddr goes over logs ignoring DEBUG lines | 146 | // ExtractListeningAddr goes over logs until a line starting with pat |
336 | 143 | // until a line starting with pat and returns the rest of that line. | 147 | // and returns the rest of that line. |
337 | 144 | func ExtractListeningAddr(c *C, logs <-chan string, pat string) string { | 148 | func ExtractListeningAddr(c *C, logs <-chan string, pat string) string { |
338 | 145 | for line := range logs { | 149 | for line := range logs { |
339 | 146 | if strings.HasPrefix(line, debugPrefix) { | ||
340 | 147 | continue | ||
341 | 148 | } | ||
342 | 149 | if !strings.HasPrefix(line, pat) { | 150 | if !strings.HasPrefix(line, pat) { |
343 | 150 | c.Fatalf("matching %v: %v", pat, line) | 151 | c.Fatalf("matching %v: %v", pat, line) |
344 | 151 | } | 152 | } |
345 | 152 | 153 | ||
346 | === modified file 'server/api/handlers.go' | |||
347 | --- server/api/handlers.go 2014-07-14 15:23:17 +0000 | |||
348 | +++ server/api/handlers.go 2014-11-13 14:37:31 +0000 | |||
349 | @@ -98,6 +98,12 @@ | |||
350 | 98 | "Wrong request method, should be POST", | 98 | "Wrong request method, should be POST", |
351 | 99 | nil, | 99 | nil, |
352 | 100 | } | 100 | } |
353 | 101 | ErrWrongRequestMethodGET = &APIError{ | ||
354 | 102 | http.StatusMethodNotAllowed, | ||
355 | 103 | invalidRequest, | ||
356 | 104 | "Wrong request method, should be GET", | ||
357 | 105 | nil, | ||
358 | 106 | } | ||
359 | 101 | ErrMalformedJSONObject = &APIError{ | 107 | ErrMalformedJSONObject = &APIError{ |
360 | 102 | http.StatusBadRequest, | 108 | http.StatusBadRequest, |
361 | 103 | invalidRequest, | 109 | invalidRequest, |
362 | @@ -452,14 +458,17 @@ | |||
363 | 452 | if err != nil { | 458 | if err != nil { |
364 | 453 | switch err { | 459 | switch err { |
365 | 454 | case store.ErrUnknownToken: | 460 | case store.ErrUnknownToken: |
366 | 461 | ctx.logger.Debugf("notify: %v %v unknown", ucast.AppId, ucast.Token) | ||
367 | 455 | return nil, ErrUnknownToken | 462 | return nil, ErrUnknownToken |
368 | 456 | case store.ErrUnauthorized: | 463 | case store.ErrUnauthorized: |
369 | 464 | ctx.logger.Debugf("notify: %v %v unauthorized", ucast.AppId, ucast.Token) | ||
370 | 457 | return nil, ErrUnauthorized | 465 | return nil, ErrUnauthorized |
371 | 458 | default: | 466 | default: |
372 | 459 | ctx.logger.Errorf("could not resolve token: %v", err) | 467 | ctx.logger.Errorf("could not resolve token: %v", err) |
373 | 460 | return nil, ErrCouldNotResolveToken | 468 | return nil, ErrCouldNotResolveToken |
374 | 461 | } | 469 | } |
375 | 462 | } | 470 | } |
376 | 471 | ctx.logger.Debugf("notify: %v %v -> %v", ucast.AppId, ucast.Token, chanId) | ||
377 | 463 | 472 | ||
378 | 464 | _, notifs, meta, err := sto.GetChannelUnfiltered(chanId) | 473 | _, notifs, meta, err := sto.GetChannelUnfiltered(chanId) |
379 | 465 | if err != nil { | 474 | if err != nil { |
380 | @@ -491,6 +500,7 @@ | |||
381 | 491 | if ucast.ClearPending { | 500 | if ucast.ClearPending { |
382 | 492 | scrubCriteria = []string{ucast.AppId} | 501 | scrubCriteria = []string{ucast.AppId} |
383 | 493 | } else if forApp >= ctx.storage.GetMaxNotificationsPerApplication() { | 502 | } else if forApp >= ctx.storage.GetMaxNotificationsPerApplication() { |
384 | 503 | ctx.logger.Debugf("notify: %v %v too many pending", ucast.AppId, chanId) | ||
385 | 494 | return nil, apiErrorWithExtra(ErrTooManyPendingNotifications, | 504 | return nil, apiErrorWithExtra(ErrTooManyPendingNotifications, |
386 | 495 | &last.Payload) | 505 | &last.Payload) |
387 | 496 | } else if replaceable > 0 { | 506 | } else if replaceable > 0 { |
388 | @@ -518,6 +528,8 @@ | |||
389 | 518 | } | 528 | } |
390 | 519 | 529 | ||
391 | 520 | ctx.broker.Unicast(chanId) | 530 | ctx.broker.Unicast(chanId) |
392 | 531 | |||
393 | 532 | ctx.logger.Debugf("notify: ok %v %v id:%v clear:%v replace:%v expired:%v", ucast.AppId, chanId, msgId, ucast.ClearPending, replaceable, expired) | ||
394 | 521 | return nil, nil | 533 | return nil, nil |
395 | 522 | } | 534 | } |
396 | 523 | 535 | ||
397 | 524 | 536 | ||
398 | === modified file 'server/api/handlers_test.go' | |||
399 | --- server/api/handlers_test.go 2014-07-15 15:03:49 +0000 | |||
400 | +++ server/api/handlers_test.go 2014-11-13 14:37:31 +0000 | |||
401 | @@ -362,7 +362,7 @@ | |||
402 | 362 | } | 362 | } |
403 | 363 | sto := store.NewInMemoryPendingStore() | 363 | sto := store.NewInMemoryPendingStore() |
404 | 364 | bsend := &checkBrokerSending{store: sto} | 364 | bsend := &checkBrokerSending{store: sto} |
406 | 365 | ctx := &context{testStoreAccess(nil), bsend, nil} | 365 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
407 | 366 | payload := json.RawMessage(`{"a": 1}`) | 366 | payload := json.RawMessage(`{"a": 1}`) |
408 | 367 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 367 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
409 | 368 | UserId: "user1", | 368 | UserId: "user1", |
410 | @@ -487,7 +487,7 @@ | |||
411 | 487 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) | 487 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
412 | 488 | 488 | ||
413 | 489 | bsend := &checkBrokerSending{store: sto} | 489 | bsend := &checkBrokerSending{store: sto} |
415 | 490 | ctx := &context{testStoreAccess(nil), bsend, nil} | 490 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
416 | 491 | payload := json.RawMessage(`{"a": 1}`) | 491 | payload := json.RawMessage(`{"a": 1}`) |
417 | 492 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 492 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
418 | 493 | UserId: "user1", | 493 | UserId: "user1", |
419 | @@ -528,7 +528,7 @@ | |||
420 | 528 | sto.AppendToUnicastChannel(chanId, "app1", n, "m1", expire) | 528 | sto.AppendToUnicastChannel(chanId, "app1", n, "m1", expire) |
421 | 529 | 529 | ||
422 | 530 | bsend := &checkBrokerSending{store: sto} | 530 | bsend := &checkBrokerSending{store: sto} |
424 | 531 | ctx := &context{testStoreAccess(nil), bsend, nil} | 531 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
425 | 532 | payload := json.RawMessage(`{"a": 1}`) | 532 | payload := json.RawMessage(`{"a": 1}`) |
426 | 533 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 533 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
427 | 534 | UserId: "user1", | 534 | UserId: "user1", |
428 | @@ -634,7 +634,7 @@ | |||
429 | 634 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) | 634 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
430 | 635 | 635 | ||
431 | 636 | bsend := &checkBrokerSending{store: sto} | 636 | bsend := &checkBrokerSending{store: sto} |
433 | 637 | ctx := &context{testStoreAccess(nil), bsend, nil} | 637 | ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
434 | 638 | payload := json.RawMessage(`{"a": 1}`) | 638 | payload := json.RawMessage(`{"a": 1}`) |
435 | 639 | res, apiErr := doUnicast(ctx, sto, &Unicast{ | 639 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
436 | 640 | UserId: "user1", | 640 | UserId: "user1", |
437 | @@ -965,7 +965,7 @@ | |||
438 | 965 | return sto, nil | 965 | return sto, nil |
439 | 966 | }) | 966 | }) |
440 | 967 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} | 967 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
442 | 968 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) | 968 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
443 | 969 | defer testServer.Close() | 969 | defer testServer.Close() |
444 | 970 | 970 | ||
445 | 971 | payload := json.RawMessage(`{"foo":"bar"}`) | 971 | payload := json.RawMessage(`{"foo":"bar"}`) |
446 | @@ -1049,7 +1049,7 @@ | |||
447 | 1049 | return sto, nil | 1049 | return sto, nil |
448 | 1050 | }) | 1050 | }) |
449 | 1051 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} | 1051 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
451 | 1052 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) | 1052 | testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
452 | 1053 | defer testServer.Close() | 1053 | defer testServer.Close() |
453 | 1054 | 1054 | ||
454 | 1055 | request := newPostRequest("/register", &Registration{ | 1055 | request := newPostRequest("/register", &Registration{ |
455 | 1056 | 1056 | ||
456 | === modified file 'server/broker/testsuite/suite.go' | |||
457 | --- server/broker/testsuite/suite.go 2014-09-25 11:16:38 +0000 | |||
458 | +++ server/broker/testsuite/suite.go 2014-11-13 14:37:31 +0000 | |||
459 | @@ -66,12 +66,12 @@ | |||
460 | 66 | 66 | ||
461 | 67 | func (s *CommonBrokerSuite) TestSanity(c *C) { | 67 | func (s *CommonBrokerSuite) TestSanity(c *C) { |
462 | 68 | sto := store.NewInMemoryPendingStore() | 68 | sto := store.NewInMemoryPendingStore() |
464 | 69 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 69 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
465 | 70 | c.Check(s.RevealSession(b, "FOO"), IsNil) | 70 | c.Check(s.RevealSession(b, "FOO"), IsNil) |
466 | 71 | } | 71 | } |
467 | 72 | 72 | ||
468 | 73 | func (s *CommonBrokerSuite) TestStartStop(c *C) { | 73 | func (s *CommonBrokerSuite) TestStartStop(c *C) { |
470 | 74 | b := s.MakeBroker(nil, testBrokerConfig, nil) | 74 | b := s.MakeBroker(nil, testBrokerConfig, s.testlog) |
471 | 75 | b.Start() | 75 | b.Start() |
472 | 76 | c.Check(b.Running(), Equals, true) | 76 | c.Check(b.Running(), Equals, true) |
473 | 77 | b.Start() | 77 | b.Start() |
474 | @@ -82,7 +82,7 @@ | |||
475 | 82 | 82 | ||
476 | 83 | func (s *CommonBrokerSuite) TestRegistration(c *C) { | 83 | func (s *CommonBrokerSuite) TestRegistration(c *C) { |
477 | 84 | sto := store.NewInMemoryPendingStore() | 84 | sto := store.NewInMemoryPendingStore() |
479 | 85 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 85 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
480 | 86 | b.Start() | 86 | b.Start() |
481 | 87 | defer b.Stop() | 87 | defer b.Stop() |
482 | 88 | sess, err := b.Register(&protocol.ConnectMsg{ | 88 | sess, err := b.Register(&protocol.ConnectMsg{ |
483 | @@ -112,7 +112,7 @@ | |||
484 | 112 | 112 | ||
485 | 113 | func (s *CommonBrokerSuite) TestRegistrationBrokenLevels(c *C) { | 113 | func (s *CommonBrokerSuite) TestRegistrationBrokenLevels(c *C) { |
486 | 114 | sto := store.NewInMemoryPendingStore() | 114 | sto := store.NewInMemoryPendingStore() |
488 | 115 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 115 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
489 | 116 | b.Start() | 116 | b.Start() |
490 | 117 | defer b.Stop() | 117 | defer b.Stop() |
491 | 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")) |
492 | @@ -121,7 +121,7 @@ | |||
493 | 121 | 121 | ||
494 | 122 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { | 122 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { |
495 | 123 | sto := store.NewInMemoryPendingStore() | 123 | sto := store.NewInMemoryPendingStore() |
497 | 124 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 124 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
498 | 125 | b.Start() | 125 | b.Start() |
499 | 126 | defer b.Stop() | 126 | defer b.Stop() |
500 | 127 | info := map[string]interface{}{ | 127 | info := map[string]interface{}{ |
501 | @@ -140,7 +140,7 @@ | |||
502 | 140 | notification1 := json.RawMessage(`{"m": "M"}`) | 140 | notification1 := json.RawMessage(`{"m": "M"}`) |
503 | 141 | muchLater := time.Now().Add(10 * time.Minute) | 141 | muchLater := time.Now().Add(10 * time.Minute) |
504 | 142 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) | 142 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) |
506 | 143 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 143 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
507 | 144 | b.Start() | 144 | b.Start() |
508 | 145 | defer b.Stop() | 145 | defer b.Stop() |
509 | 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")) |
510 | @@ -166,7 +166,7 @@ | |||
511 | 166 | 166 | ||
512 | 167 | func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) { | 167 | func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) { |
513 | 168 | sto := store.NewInMemoryPendingStore() | 168 | sto := store.NewInMemoryPendingStore() |
515 | 169 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 169 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
516 | 170 | b.Start() | 170 | b.Start() |
517 | 171 | defer b.Stop() | 171 | defer b.Stop() |
518 | 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")) |
519 | @@ -197,7 +197,7 @@ | |||
520 | 197 | sto := store.NewInMemoryPendingStore() | 197 | sto := store.NewInMemoryPendingStore() |
521 | 198 | notification1 := json.RawMessage(`{"m": "M"}`) | 198 | notification1 := json.RawMessage(`{"m": "M"}`) |
522 | 199 | decoded1 := map[string]interface{}{"m": "M"} | 199 | decoded1 := map[string]interface{}{"m": "M"} |
524 | 200 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 200 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
525 | 201 | b.Start() | 201 | b.Start() |
526 | 202 | defer b.Stop() | 202 | defer b.Stop() |
527 | 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")) |
528 | @@ -278,7 +278,7 @@ | |||
529 | 278 | notification2 := json.RawMessage(`{"m": "M2"}`) | 278 | notification2 := json.RawMessage(`{"m": "M2"}`) |
530 | 279 | chanId1 := store.UnicastInternalChannelId("dev1", "dev1") | 279 | chanId1 := store.UnicastInternalChannelId("dev1", "dev1") |
531 | 280 | chanId2 := store.UnicastInternalChannelId("dev2", "dev2") | 280 | chanId2 := store.UnicastInternalChannelId("dev2", "dev2") |
533 | 281 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 281 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
534 | 282 | b.Start() | 282 | b.Start() |
535 | 283 | defer b.Stop() | 283 | defer b.Stop() |
536 | 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")) |
537 | @@ -312,7 +312,7 @@ | |||
538 | 312 | sto := store.NewInMemoryPendingStore() | 312 | sto := store.NewInMemoryPendingStore() |
539 | 313 | notification1 := json.RawMessage(`{"m": "M1"}`) | 313 | notification1 := json.RawMessage(`{"m": "M1"}`) |
540 | 314 | chanId1 := store.UnicastInternalChannelId("dev3", "dev3") | 314 | chanId1 := store.UnicastInternalChannelId("dev3", "dev3") |
542 | 315 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 315 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
543 | 316 | b.Start() | 316 | b.Start() |
544 | 317 | defer b.Stop() | 317 | defer b.Stop() |
545 | 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")) |
546 | @@ -354,7 +354,7 @@ | |||
547 | 354 | 354 | ||
548 | 355 | func (s *CommonBrokerSuite) TestSessionFeed(c *C) { | 355 | func (s *CommonBrokerSuite) TestSessionFeed(c *C) { |
549 | 356 | sto := store.NewInMemoryPendingStore() | 356 | sto := store.NewInMemoryPendingStore() |
551 | 357 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 357 | b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
552 | 358 | b.Start() | 358 | b.Start() |
553 | 359 | defer b.Stop() | 359 | defer b.Stop() |
554 | 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")) |