Merge lp:~pedronis/ubuntu-push/refactor-service-setup into lp:ubuntu-push
- refactor-service-setup
- Merge into trunk
Proposed by
Samuele Pedroni
Status: | Superseded |
---|---|
Proposed branch: | lp:~pedronis/ubuntu-push/refactor-service-setup |
Merge into: | lp:ubuntu-push |
Diff against target: |
823 lines (+285/-178) 8 files modified
client/client.go (+28/-10) client/client_test.go (+64/-7) client/service/postal.go (+14/-21) client/service/postal_test.go (+32/-0) client/service/service.go (+54/-68) client/service/service_test.go (+82/-71) debian/config.json (+1/-1) testing/helpers.go (+10/-0) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/refactor-service-setup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email: mp+225477@code.launchpad.net |
This proposal has been superseded by a proposal from 2014-07-03.
Commit message
Description of the change
* let take NewPushService a setup obj, have a generic manageReg
* update config.json now that registration is a base url
To post a comment you must log in.
- 217. By Samuele Pedroni
-
wait for inc elapsed time
- 218. By Samuele Pedroni
-
Unregister method
- 219. By Samuele Pedroni
-
Merged ubuntu-push into unregister.
- 220. By Samuele Pedroni
-
tweak test
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'client/client.go' |
2 | --- client/client.go 2014-07-01 11:55:30 +0000 |
3 | +++ client/client.go 2014-07-03 14:00:43 +0000 |
4 | @@ -26,6 +26,7 @@ |
5 | "errors" |
6 | "fmt" |
7 | "io/ioutil" |
8 | + "net/url" |
9 | "os" |
10 | "os/exec" |
11 | "strings" |
12 | @@ -163,6 +164,19 @@ |
13 | } |
14 | } |
15 | |
16 | +// derivePushServiceSetup derives the service setup from the client configuration bits. |
17 | +func (client *PushClient) derivePushServiceSetup() (*service.PushServiceSetup, error) { |
18 | + setup := new(service.PushServiceSetup) |
19 | + purl, err := url.Parse(client.config.RegistrationURL) |
20 | + if err != nil { |
21 | + return nil, fmt.Errorf("cannot parse registration url: %v", err) |
22 | + } |
23 | + setup.RegURL = purl |
24 | + setup.DeviceId = client.deviceId |
25 | + setup.AuthGetter = client.getAuthorization |
26 | + return setup, nil |
27 | +} |
28 | + |
29 | // getAuthorization gets the authorization blob to send to the server |
30 | func (client *PushClient) getAuthorization(url string) string { |
31 | client.log.Debugf("getting authorization for %s", url) |
32 | @@ -320,9 +334,7 @@ |
33 | if !client.filterBroadcastNotification(msg) { |
34 | return nil |
35 | } |
36 | - not_id, err := client.postalService.SendNotification(service.ACTION_ID_BROADCAST, |
37 | - "update_manager_icon", "There's an updated system image.", |
38 | - "Tap to open the system updater.") |
39 | + not_id, err := client.postalService.InjectBroadcast() |
40 | if err != nil { |
41 | client.log.Errorf("showing notification: %s", err) |
42 | return err |
43 | @@ -338,15 +350,20 @@ |
44 | } |
45 | |
46 | // handleClick deals with the user clicking a notification |
47 | -func (client *PushClient) handleClick(action_id string) error { |
48 | +func (client *PushClient) handleClick(actionId string) error { |
49 | // “The string is a stark data structure and everywhere it is passed |
50 | // there is much duplication of process. It is a perfect vehicle for |
51 | // hiding information.” |
52 | // |
53 | // From ACM's SIGPLAN publication, (September, 1982), Article |
54 | // "Epigrams in Programming", by Alan J. Perlis of Yale University. |
55 | - url := strings.TrimPrefix(action_id, service.ACTION_ID_SNOWFLAKE) |
56 | - if len(url) == len(action_id) || len(url) == 0 { |
57 | + url := actionId |
58 | + // XXX: branch for the broadcast notifications |
59 | + if strings.HasPrefix(actionId, service.ACTION_ID_PREFIX) { |
60 | + parts := strings.Split(actionId, "::") |
61 | + url = parts[1] |
62 | + } |
63 | + if len(url) == len(actionId) || len(url) == 0 { |
64 | // it didn't start with the prefix |
65 | return nil |
66 | } |
67 | @@ -396,6 +413,10 @@ |
68 | } |
69 | |
70 | func (client *PushClient) startService() error { |
71 | + setup, err := client.derivePushServiceSetup() |
72 | + if err != nil { |
73 | + return err |
74 | + } |
75 | if client.pushServiceEndpoint == nil { |
76 | client.pushServiceEndpoint = bus.SessionBus.Endpoint(service.PushServiceBusAddress, client.log) |
77 | } |
78 | @@ -403,10 +424,7 @@ |
79 | client.postalServiceEndpoint = bus.SessionBus.Endpoint(service.PostalServiceBusAddress, client.log) |
80 | } |
81 | |
82 | - client.pushService = service.NewPushService(client.pushServiceEndpoint, client.log) |
83 | - client.pushService.SetRegistrationURL(client.config.RegistrationURL) |
84 | - client.pushService.SetAuthGetter(client.getAuthorization) |
85 | - client.pushService.SetDeviceId(client.deviceId) |
86 | + client.pushService = service.NewPushService(client.pushServiceEndpoint, setup, client.log) |
87 | if err := client.pushService.Start(); err != nil { |
88 | return err |
89 | } |
90 | |
91 | === modified file 'client/client_test.go' |
92 | --- client/client_test.go 2014-07-01 11:55:30 +0000 |
93 | +++ client/client_test.go 2014-07-03 14:00:43 +0000 |
94 | @@ -289,7 +289,7 @@ |
95 | // finally compare |
96 | conf := cli.deriveSessionConfig(info) |
97 | // compare authGetter by string |
98 | - c.Check(fmt.Sprintf("%v", conf.AuthGetter), Equals, fmt.Sprintf("%v", cli.getAuthorization)) |
99 | + c.Check(fmt.Sprintf("%#v", conf.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization)) |
100 | // and set it to nil |
101 | conf.AuthGetter = nil |
102 | expected.AuthGetter = nil |
103 | @@ -297,6 +297,51 @@ |
104 | } |
105 | |
106 | /***************************************************************** |
107 | + derivePushServiceSetup tests |
108 | +******************************************************************/ |
109 | + |
110 | +func (cs *clientSuite) TestDerivePushServiceSetup(c *C) { |
111 | + cs.writeTestConfig(map[string]interface{}{}) |
112 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
113 | + err := cli.configure() |
114 | + c.Assert(err, IsNil) |
115 | + cli.deviceId = "zoo" |
116 | + expected := &service.PushServiceSetup{ |
117 | + DeviceId: "zoo", |
118 | + AuthGetter: func(string) string { return "" }, |
119 | + RegURL: helpers.ParseURL("reg://"), |
120 | + } |
121 | + // sanity check that we are looking at all fields |
122 | + vExpected := reflect.ValueOf(expected).Elem() |
123 | + nf := vExpected.NumField() |
124 | + for i := 0; i < nf; i++ { |
125 | + fv := vExpected.Field(i) |
126 | + // field isn't empty/zero |
127 | + c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name)) |
128 | + } |
129 | + // finally compare |
130 | + setup, err := cli.derivePushServiceSetup() |
131 | + c.Assert(err, IsNil) |
132 | + // compare authGetter by string |
133 | + c.Check(fmt.Sprintf("%#v", setup.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization)) |
134 | + // and set it to nil |
135 | + setup.AuthGetter = nil |
136 | + expected.AuthGetter = nil |
137 | + c.Check(setup, DeepEquals, expected) |
138 | +} |
139 | + |
140 | +func (cs *clientSuite) TestDerivePushServiceSetupError(c *C) { |
141 | + cs.writeTestConfig(map[string]interface{}{ |
142 | + "registration_url": "%gh", |
143 | + }) |
144 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
145 | + err := cli.configure() |
146 | + c.Assert(err, IsNil) |
147 | + _, err = cli.derivePushServiceSetup() |
148 | + c.Check(err, ErrorMatches, "cannot parse registration url:.*") |
149 | +} |
150 | + |
151 | +/***************************************************************** |
152 | startService tests |
153 | ******************************************************************/ |
154 | |
155 | @@ -305,7 +350,8 @@ |
156 | "auth_helper": helpers.ScriptAbsPath("dummyauth.sh"), |
157 | }) |
158 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
159 | - cli.configure() |
160 | + err := cli.configure() |
161 | + c.Assert(err, IsNil) |
162 | cli.log = cs.log |
163 | cli.deviceId = "fake-id" |
164 | cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil) |
165 | @@ -314,8 +360,6 @@ |
166 | c.Check(cli.startService(), IsNil) |
167 | c.Assert(cli.pushService, NotNil) |
168 | c.Check(cli.pushService.IsRunning(), Equals, true) |
169 | - c.Check(cli.pushService.GetDeviceId(), Equals, "fake-id") |
170 | - c.Check(cli.pushService.GetRegistrationAuthorization(), Equals, "hello reg://") |
171 | c.Assert(cli.setupPostalService(), IsNil) |
172 | c.Assert(cli.startPostalService(), IsNil) |
173 | c.Check(cli.postalService.IsRunning(), Equals, true) |
174 | @@ -323,6 +367,17 @@ |
175 | cli.postalService.Stop() |
176 | } |
177 | |
178 | +func (cs *clientSuite) TestStartServiceSetupError(c *C) { |
179 | + cs.writeTestConfig(map[string]interface{}{ |
180 | + "registration_url": "%gh", |
181 | + }) |
182 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
183 | + err := cli.configure() |
184 | + c.Assert(err, IsNil) |
185 | + err = cli.startService() |
186 | + c.Check(err, ErrorMatches, "cannot parse registration url:.*") |
187 | +} |
188 | + |
189 | func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) { |
190 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
191 | c.Check(cli.log, IsNil) |
192 | @@ -712,6 +767,8 @@ |
193 | handleClick tests |
194 | ******************************************************************/ |
195 | |
196 | +var ACTION_ID_BROADCAST = service.ACTION_ID_PREFIX + service.SystemUpdateUrl + service.ACTION_ID_SUFFIX |
197 | + |
198 | func (cs *clientSuite) TestHandleClick(c *C) { |
199 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
200 | cli.log = cs.log |
201 | @@ -723,14 +780,14 @@ |
202 | args := testibus.GetCallArgs(endp) |
203 | c.Assert(args, HasLen, 0) |
204 | // check we worked with the right action id |
205 | - c.Check(cli.handleClick(service.ACTION_ID_BROADCAST), IsNil) |
206 | + c.Check(cli.handleClick(ACTION_ID_BROADCAST), IsNil) |
207 | // check we sent the notification |
208 | args = testibus.GetCallArgs(endp) |
209 | c.Assert(args, HasLen, 1) |
210 | c.Check(args[0].Member, Equals, "DispatchURL") |
211 | c.Check(args[0].Args, DeepEquals, []interface{}{service.SystemUpdateUrl}) |
212 | // check we worked with the right action id |
213 | - c.Check(cli.handleClick(service.ACTION_ID_SNOWFLAKE+"foo"), IsNil) |
214 | + c.Check(cli.handleClick(service.ACTION_ID_PREFIX+"foo"), IsNil) |
215 | // check we sent the notification |
216 | args = testibus.GetCallArgs(endp) |
217 | c.Assert(args, HasLen, 2) |
218 | @@ -879,7 +936,7 @@ |
219 | c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$") |
220 | |
221 | // * actionsCh to the click handler/url dispatcher |
222 | - aCh <- notifications.RawActionReply{ActionId: service.ACTION_ID_BROADCAST} |
223 | + aCh <- notifications.RawActionReply{ActionId: ACTION_ID_BROADCAST} |
224 | tick() |
225 | uargs := testibus.GetCallArgs(cli.urlDispatcherEndp) |
226 | c.Assert(uargs, HasLen, 1) |
227 | |
228 | === modified file 'client/service/postal.go' |
229 | --- client/service/postal.go 2014-07-01 11:55:30 +0000 |
230 | +++ client/service/postal.go 2014-07-03 14:00:43 +0000 |
231 | @@ -20,7 +20,6 @@ |
232 | "strings" |
233 | |
234 | "code.google.com/p/go-uuid/uuid" |
235 | - "launchpad.net/go-dbus/v1" |
236 | |
237 | "launchpad.net/ubuntu-push/bus" |
238 | "launchpad.net/ubuntu-push/bus/notifications" |
239 | @@ -50,9 +49,9 @@ |
240 | ) |
241 | |
242 | var ( |
243 | - SystemUpdateUrl = "settings:///system/system-update" |
244 | - ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::" |
245 | - ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + SystemUpdateUrl |
246 | + SystemUpdateUrl = "settings:///system/system-update" |
247 | + ACTION_ID_PREFIX = "ubuntu-push-client::" |
248 | + ACTION_ID_SUFFIX = "::0" |
249 | ) |
250 | |
251 | // NewPostalService() builds a new service and returns it. |
252 | @@ -90,9 +89,7 @@ |
253 | } |
254 | |
255 | func (svc *PostalService) TakeTheBus() (<-chan notifications.RawActionReply, error) { |
256 | - iniCh := make(chan uint32) |
257 | - go func() { iniCh <- util.NewAutoRedialer(svc.notificationsEndp).Redial() }() |
258 | - <-iniCh |
259 | + util.NewAutoRedialer(svc.notificationsEndp).Redial() |
260 | actionsCh, err := notifications.Raw(svc.notificationsEndp, svc.Log).WatchActions() |
261 | return actionsCh, err |
262 | } |
263 | @@ -164,18 +161,14 @@ |
264 | return err |
265 | } |
266 | |
267 | -func (svc *PostalService) SendNotification(action_id, icon, summary, body string) (uint32, error) { |
268 | - a := []string{action_id, "Switch to app"} // action value not visible on the phone |
269 | - h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} |
270 | - nots := notifications.Raw(svc.notificationsEndp, svc.Log) |
271 | - return nots.Notify( |
272 | - "ubuntu-push-client", // app name |
273 | - uint32(0), // id |
274 | - icon, // icon |
275 | - summary, // summary |
276 | - body, // body |
277 | - a, // actions |
278 | - h, // hints |
279 | - int32(10*1000), // timeout (ms) |
280 | - ) |
281 | +func (svc *PostalService) InjectBroadcast() (uint32, error) { |
282 | + // XXX: call a helper? |
283 | + // XXX: Present force us to send the url as the notificationId |
284 | + icon := "update_manager_icon" |
285 | + summary := "There's an updated system image." |
286 | + body := "Tap to open the system updater." |
287 | + actions := []string{"Switch to app"} // action value not visible on the phone |
288 | + card := &launch_helper.Card{Icon: icon, Summary: summary, Body: body, Actions: actions, Popup: true} |
289 | + output := &launch_helper.HelperOutput{[]byte(""), &launch_helper.Notification{Card: card}} |
290 | + return 0, svc.msgHandler("ubuntu-push-client", SystemUpdateUrl, output) |
291 | } |
292 | |
293 | === modified file 'client/service/postal_test.go' |
294 | --- client/service/postal_test.go 2014-07-01 11:55:30 +0000 |
295 | +++ client/service/postal_test.go 2014-07-03 14:00:43 +0000 |
296 | @@ -157,6 +157,38 @@ |
297 | } |
298 | |
299 | // |
300 | +// Injection (Broadcast) tests |
301 | + |
302 | +func (ss *postalSuite) TestInjectBroadcast(c *C) { |
303 | + bus := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
304 | + svc := NewPostalService(ss.bus, bus, ss.log) |
305 | + //svc.msgHandler = nil |
306 | + rvs, err := svc.InjectBroadcast() |
307 | + c.Assert(err, IsNil) |
308 | + c.Check(rvs, Equals, uint32(0)) |
309 | + c.Assert(err, IsNil) |
310 | + // and check it fired the right signal (twice) |
311 | + callArgs := testibus.GetCallArgs(bus) |
312 | + c.Assert(callArgs, HasLen, 1) |
313 | + c.Check(callArgs[0].Member, Equals, "Notify") |
314 | + c.Check(callArgs[0].Args[0:6], DeepEquals, []interface{}{"ubuntu-push-client", uint32(0), "update_manager_icon", |
315 | + "There's an updated system image.", "Tap to open the system updater.", |
316 | + []string{"ubuntu-push-client::settings:///system/system-update::0", "Switch to app"}}) |
317 | + // TODO: check the map in callArgs? |
318 | + // c.Check(callArgs[0].Args[7]["x-canonical-secondary-icon"], NotNil) |
319 | + // c.Check(callArgs[0].Args[7]["x-canonical-snap-decisions"], NotNil) |
320 | +} |
321 | + |
322 | +func (ss *postalSuite) TestInjectBroadcastFails(c *C) { |
323 | + bus := testibus.NewTestingEndpoint(condition.Work(true), |
324 | + condition.Work(false)) |
325 | + svc := NewPostalService(ss.bus, bus, ss.log) |
326 | + svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return errors.New("fail") }) |
327 | + _, err := svc.InjectBroadcast() |
328 | + c.Check(err, NotNil) |
329 | +} |
330 | + |
331 | +// |
332 | // Notifications tests |
333 | func (ss *postalSuite) TestNotificationsWorks(c *C) { |
334 | svc := NewPostalService(ss.bus, ss.notifBus, ss.log) |
335 | |
336 | === modified file 'client/service/service.go' |
337 | --- client/service/service.go 2014-07-01 11:55:30 +0000 |
338 | +++ client/service/service.go 2014-07-03 14:00:43 +0000 |
339 | @@ -23,6 +23,7 @@ |
340 | "fmt" |
341 | "io/ioutil" |
342 | "net/http" |
343 | + "net/url" |
344 | "os" |
345 | "strings" |
346 | |
347 | @@ -32,10 +33,17 @@ |
348 | "launchpad.net/ubuntu-push/nih" |
349 | ) |
350 | |
351 | +// PushServiceSetup encapsulates the params for setting up a PushService. |
352 | +type PushServiceSetup struct { |
353 | + RegURL *url.URL |
354 | + DeviceId string |
355 | + AuthGetter func(string) string |
356 | +} |
357 | + |
358 | // PushService is the dbus api |
359 | type PushService struct { |
360 | DBusService |
361 | - regURL string |
362 | + regURL *url.URL |
363 | deviceId string |
364 | authGetter func(string) string |
365 | httpCli http13.Client |
366 | @@ -50,59 +58,28 @@ |
367 | ) |
368 | |
369 | // NewPushService() builds a new service and returns it. |
370 | -func NewPushService(bus bus.Endpoint, log logger.Logger) *PushService { |
371 | +func NewPushService(bus bus.Endpoint, setup *PushServiceSetup, log logger.Logger) *PushService { |
372 | var svc = &PushService{} |
373 | svc.Log = log |
374 | svc.Bus = bus |
375 | + svc.regURL = setup.RegURL |
376 | + svc.deviceId = setup.DeviceId |
377 | + svc.authGetter = setup.AuthGetter |
378 | return svc |
379 | } |
380 | |
381 | -// SetRegistrationURL() sets the registration url for the service |
382 | -func (svc *PushService) SetRegistrationURL(url string) { |
383 | - svc.lock.Lock() |
384 | - defer svc.lock.Unlock() |
385 | - svc.regURL = url |
386 | -} |
387 | - |
388 | -// SetAuthGetter() sets the authorization getter for the service |
389 | -func (svc *PushService) SetAuthGetter(authGetter func(string) string) { |
390 | - svc.lock.Lock() |
391 | - defer svc.lock.Unlock() |
392 | - svc.authGetter = authGetter |
393 | -} |
394 | - |
395 | -// getRegistrationAuthorization() returns the authorization header for |
396 | -// POSTing to the registration HTTP endpoint |
397 | -// |
398 | -// (this is for calling with the lock held) |
399 | -func (svc *PushService) getRegistrationAuthorization() string { |
400 | - if svc.authGetter != nil && svc.regURL != "" { |
401 | - return svc.authGetter(svc.regURL) |
402 | - } else { |
403 | - return "" |
404 | - } |
405 | -} |
406 | - |
407 | -// GetRegistrationAuthorization() returns the authorization header for |
408 | -// POSTing to the registration HTTP endpoint |
409 | -func (svc *PushService) GetRegistrationAuthorization() string { |
410 | - svc.lock.RLock() |
411 | - defer svc.lock.RUnlock() |
412 | - return svc.getRegistrationAuthorization() |
413 | -} |
414 | - |
415 | -// SetDeviceId() sets the device id |
416 | -func (svc *PushService) SetDeviceId(deviceId string) { |
417 | - svc.lock.Lock() |
418 | - defer svc.lock.Unlock() |
419 | - svc.deviceId = deviceId |
420 | -} |
421 | - |
422 | -// GetDeviceId() returns the device id |
423 | -func (svc *PushService) GetDeviceId() string { |
424 | - svc.lock.RLock() |
425 | - defer svc.lock.RUnlock() |
426 | - return svc.deviceId |
427 | +// getAuthorization() returns the URL and the authorization header for |
428 | +// POSTing to the registration HTTP endpoint for op |
429 | +func (svc *PushService) getAuthorization(op string) (string, string) { |
430 | + if svc.authGetter == nil || svc.regURL == nil { |
431 | + return "", "" |
432 | + } |
433 | + purl, err := svc.regURL.Parse(op) |
434 | + if err != nil { |
435 | + panic("op to getAuthorization was invalid") |
436 | + } |
437 | + url := purl.String() |
438 | + return url, svc.authGetter(url) |
439 | } |
440 | |
441 | func (svc *PushService) Start() error { |
442 | @@ -130,32 +107,21 @@ |
443 | Message string `json:"message"` // |
444 | } |
445 | |
446 | -func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) { |
447 | - svc.lock.RLock() |
448 | - defer svc.lock.RUnlock() |
449 | - if len(args) != 0 { |
450 | - return nil, BadArgCount |
451 | - } |
452 | - raw_appname := path[strings.LastIndex(path, "/")+1:] |
453 | - appname := string(nih.Unquote([]byte(raw_appname))) |
454 | - |
455 | - rv := os.Getenv("PUSH_REG_" + raw_appname) |
456 | - if rv != "" { |
457 | - return []interface{}{rv}, nil |
458 | - } |
459 | - |
460 | - req_body, err := json.Marshal(registrationRequest{svc.deviceId, appname}) |
461 | +func (svc *PushService) manageReg(op, appId string) (*registrationReply, error) { |
462 | + req_body, err := json.Marshal(registrationRequest{svc.deviceId, appId}) |
463 | if err != nil { |
464 | return nil, fmt.Errorf("unable to marshal register request body: %v", err) |
465 | } |
466 | - req, err := http13.NewRequest("POST", svc.regURL, bytes.NewReader(req_body)) |
467 | - if err != nil { |
468 | - return nil, fmt.Errorf("unable to build register request: %v", err) |
469 | - } |
470 | - auth := svc.getRegistrationAuthorization() |
471 | + |
472 | + url, auth := svc.getAuthorization(op) |
473 | if auth == "" { |
474 | return nil, BadAuth |
475 | } |
476 | + |
477 | + req, err := http13.NewRequest("POST", url, bytes.NewReader(req_body)) |
478 | + if err != nil { |
479 | + panic(fmt.Errorf("unable to build register request: %v", err)) |
480 | + } |
481 | req.Header.Add("Authorization", auth) |
482 | req.Header.Add("Content-Type", "application/json") |
483 | |
484 | @@ -188,6 +154,26 @@ |
485 | return nil, fmt.Errorf("unable to unmarshal register response: %v", err) |
486 | } |
487 | |
488 | + return &reply, nil |
489 | +} |
490 | + |
491 | +func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) { |
492 | + if len(args) != 0 { |
493 | + return nil, BadArgCount |
494 | + } |
495 | + raw_appname := path[strings.LastIndex(path, "/")+1:] |
496 | + appname := string(nih.Unquote([]byte(raw_appname))) |
497 | + |
498 | + rv := os.Getenv("PUSH_REG_" + raw_appname) |
499 | + if rv != "" { |
500 | + return []interface{}{rv}, nil |
501 | + } |
502 | + |
503 | + reply, err := svc.manageReg("/register", appname) |
504 | + if err != nil { |
505 | + return nil, err |
506 | + } |
507 | + |
508 | if !reply.Ok || reply.Token == "" { |
509 | svc.Log.Errorf("Unexpected response: %#v", reply) |
510 | return nil, BadToken |
511 | |
512 | === modified file 'client/service/service_test.go' |
513 | --- client/service/service_test.go 2014-06-20 12:33:03 +0000 |
514 | +++ client/service/service_test.go 2014-07-03 14:00:43 +0000 |
515 | @@ -47,8 +47,25 @@ |
516 | ss.bus = testibus.NewTestingEndpoint(condition.Work(true), nil) |
517 | } |
518 | |
519 | +var testSetup = &PushServiceSetup{} |
520 | + |
521 | +func (ss *serviceSuite) TestBuild(c *C) { |
522 | + setup := &PushServiceSetup{ |
523 | + RegURL: helpers.ParseURL("http://reg"), |
524 | + DeviceId: "FOO", |
525 | + AuthGetter: func(s string) string { |
526 | + return "" |
527 | + }, |
528 | + } |
529 | + svc := NewPushService(ss.bus, setup, ss.log) |
530 | + c.Check(svc.Bus, Equals, ss.bus) |
531 | + c.Check(svc.regURL, DeepEquals, helpers.ParseURL("http://reg")) |
532 | + c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", setup.AuthGetter)) |
533 | + // ... |
534 | +} |
535 | + |
536 | func (ss *serviceSuite) TestStart(c *C) { |
537 | - svc := NewPushService(ss.bus, ss.log) |
538 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
539 | c.Check(svc.IsRunning(), Equals, false) |
540 | c.Check(svc.Start(), IsNil) |
541 | c.Check(svc.IsRunning(), Equals, true) |
542 | @@ -56,31 +73,31 @@ |
543 | } |
544 | |
545 | func (ss *serviceSuite) TestStartTwice(c *C) { |
546 | - svc := NewPushService(ss.bus, ss.log) |
547 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
548 | c.Check(svc.Start(), IsNil) |
549 | c.Check(svc.Start(), Equals, AlreadyStarted) |
550 | svc.Stop() |
551 | } |
552 | |
553 | func (ss *serviceSuite) TestStartNoLog(c *C) { |
554 | - svc := NewPushService(ss.bus, nil) |
555 | + svc := NewPushService(ss.bus, testSetup, nil) |
556 | c.Check(svc.Start(), Equals, NotConfigured) |
557 | } |
558 | |
559 | func (ss *serviceSuite) TestStartNoBus(c *C) { |
560 | - svc := NewPushService(nil, ss.log) |
561 | + svc := NewPushService(nil, testSetup, ss.log) |
562 | c.Check(svc.Start(), Equals, NotConfigured) |
563 | } |
564 | |
565 | func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) { |
566 | bus := testibus.NewTestingEndpoint(condition.Work(false), nil) |
567 | - svc := NewPushService(bus, ss.log) |
568 | + svc := NewPushService(bus, testSetup, ss.log) |
569 | c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`) |
570 | svc.Stop() |
571 | } |
572 | |
573 | func (ss *serviceSuite) TestStartGrabsName(c *C) { |
574 | - svc := NewPushService(ss.bus, ss.log) |
575 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
576 | c.Assert(svc.Start(), IsNil) |
577 | callArgs := testibus.GetCallArgs(ss.bus) |
578 | defer svc.Stop() |
579 | @@ -89,7 +106,7 @@ |
580 | } |
581 | |
582 | func (ss *serviceSuite) TestStopClosesBus(c *C) { |
583 | - svc := NewPushService(ss.bus, ss.log) |
584 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
585 | c.Assert(svc.Start(), IsNil) |
586 | svc.Stop() |
587 | callArgs := testibus.GetCallArgs(ss.bus) |
588 | @@ -99,35 +116,27 @@ |
589 | |
590 | // registration tests |
591 | |
592 | -func (ss *serviceSuite) TestSetRegURLWorks(c *C) { |
593 | - svc := NewPushService(ss.bus, ss.log) |
594 | - c.Check(svc.regURL, Equals, "") |
595 | - svc.SetRegistrationURL("xyzzy://") |
596 | - c.Check(svc.regURL, Equals, "xyzzy://") |
597 | -} |
598 | - |
599 | -func (ss *serviceSuite) TestSetAuthGetterWorks(c *C) { |
600 | - svc := NewPushService(ss.bus, ss.log) |
601 | - c.Check(svc.authGetter, IsNil) |
602 | - f := func(string) string { return "" } |
603 | - svc.SetAuthGetter(f) |
604 | - c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", f)) |
605 | -} |
606 | - |
607 | func (ss *serviceSuite) TestGetRegAuthWorks(c *C) { |
608 | - svc := NewPushService(ss.bus, ss.log) |
609 | - svc.SetRegistrationURL("xyzzy://") |
610 | ch := make(chan string, 1) |
611 | - f := func(s string) string { ch <- s; return "Auth " + s } |
612 | - svc.SetAuthGetter(f) |
613 | - c.Check(svc.getRegistrationAuthorization(), Equals, "Auth xyzzy://") |
614 | + setup := &PushServiceSetup{ |
615 | + RegURL: helpers.ParseURL("http://foo"), |
616 | + AuthGetter: func(s string) string { |
617 | + ch <- s |
618 | + return "Auth " + s |
619 | + }, |
620 | + } |
621 | + svc := NewPushService(ss.bus, setup, ss.log) |
622 | + url, auth := svc.getAuthorization("/op") |
623 | + c.Check(auth, Equals, "Auth http://foo/op") |
624 | c.Assert(len(ch), Equals, 1) |
625 | - c.Check(<-ch, Equals, "xyzzy://") |
626 | + c.Check(<-ch, Equals, "http://foo/op") |
627 | + c.Check(url, Equals, "http://foo/op") |
628 | } |
629 | |
630 | func (ss *serviceSuite) TestGetRegAuthDoesNotPanic(c *C) { |
631 | - svc := NewPushService(ss.bus, ss.log) |
632 | - c.Check(svc.getRegistrationAuthorization(), Equals, "") |
633 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
634 | + _, auth := svc.getAuthorization("/op") |
635 | + c.Check(auth, Equals, "") |
636 | } |
637 | |
638 | func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) { |
639 | @@ -149,11 +158,12 @@ |
640 | fmt.Fprintln(w, `{"ok":true,"token":"blob-of-bytes"}`) |
641 | })) |
642 | defer ts.Close() |
643 | - |
644 | - svc := NewPushService(ss.bus, ss.log) |
645 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
646 | - svc.SetRegistrationURL(ts.URL) |
647 | - svc.SetDeviceId("fake-device-id") |
648 | + setup := &PushServiceSetup{ |
649 | + DeviceId: "fake-device-id", |
650 | + RegURL: helpers.ParseURL(ts.URL), |
651 | + AuthGetter: func(string) string { return "tok" }, |
652 | + } |
653 | + svc := NewPushService(ss.bus, setup, ss.log) |
654 | // this'll check (un)quoting, too |
655 | reg, err := svc.register("/an_2dapp_2did", nil, nil) |
656 | c.Assert(err, IsNil) |
657 | @@ -175,60 +185,59 @@ |
658 | c.Check(err, IsNil) |
659 | } |
660 | |
661 | -func (ss *serviceSuite) TestRegistrationFailsOnBadReqURL(c *C) { |
662 | - svc := NewPushService(ss.bus, ss.log) |
663 | - svc.SetRegistrationURL("%gh") |
664 | - reg, err := svc.register("thing", nil, nil) |
665 | - c.Check(reg, IsNil) |
666 | - c.Check(err, ErrorMatches, "unable to build register request: .*") |
667 | -} |
668 | - |
669 | -func (ss *serviceSuite) TestRegistrationFailsOnBadAuth(c *C) { |
670 | - svc := NewPushService(ss.bus, ss.log) |
671 | +func (ss *serviceSuite) TestManageRegFailsOnBadAuth(c *C) { |
672 | // ... no auth added |
673 | + svc := NewPushService(ss.bus, testSetup, ss.log) |
674 | reg, err := svc.register("thing", nil, nil) |
675 | c.Check(reg, IsNil) |
676 | c.Check(err, Equals, BadAuth) |
677 | } |
678 | |
679 | -func (ss *serviceSuite) TestRegistrationFailsOnNoServer(c *C) { |
680 | - svc := NewPushService(ss.bus, ss.log) |
681 | - svc.SetRegistrationURL("xyzzy://") |
682 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
683 | +func (ss *serviceSuite) TestManageRegFailsOnNoServer(c *C) { |
684 | + setup := &PushServiceSetup{ |
685 | + DeviceId: "fake-device-id", |
686 | + RegURL: helpers.ParseURL("xyzzy://"), |
687 | + AuthGetter: func(string) string { return "tok" }, |
688 | + } |
689 | + svc := NewPushService(ss.bus, setup, ss.log) |
690 | reg, err := svc.register("thing", nil, nil) |
691 | c.Check(reg, IsNil) |
692 | c.Check(err, ErrorMatches, "unable to request registration: .*") |
693 | } |
694 | |
695 | -func (ss *serviceSuite) TestRegistrationFailsOn40x(c *C) { |
696 | +func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { |
697 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
698 | http.Error(w, "I'm a teapot", 418) |
699 | })) |
700 | defer ts.Close() |
701 | - |
702 | - svc := NewPushService(ss.bus, ss.log) |
703 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
704 | - svc.SetRegistrationURL(ts.URL) |
705 | + setup := &PushServiceSetup{ |
706 | + DeviceId: "fake-device-id", |
707 | + RegURL: helpers.ParseURL(ts.URL), |
708 | + AuthGetter: func(string) string { return "tok" }, |
709 | + } |
710 | + svc := NewPushService(ss.bus, setup, ss.log) |
711 | reg, err := svc.register("/thing", nil, nil) |
712 | c.Check(err, Equals, BadRequest) |
713 | c.Check(reg, IsNil) |
714 | } |
715 | |
716 | -func (ss *serviceSuite) TestRegistrationFailsOn50x(c *C) { |
717 | +func (ss *serviceSuite) TestManageRegFailsOn50x(c *C) { |
718 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
719 | http.Error(w, "Not implemented", 501) |
720 | })) |
721 | defer ts.Close() |
722 | - |
723 | - svc := NewPushService(ss.bus, ss.log) |
724 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
725 | - svc.SetRegistrationURL(ts.URL) |
726 | + setup := &PushServiceSetup{ |
727 | + DeviceId: "fake-device-id", |
728 | + RegURL: helpers.ParseURL(ts.URL), |
729 | + AuthGetter: func(string) string { return "tok" }, |
730 | + } |
731 | + svc := NewPushService(ss.bus, setup, ss.log) |
732 | reg, err := svc.register("/thing", nil, nil) |
733 | c.Check(err, Equals, BadServer) |
734 | c.Check(reg, IsNil) |
735 | } |
736 | |
737 | -func (ss *serviceSuite) TestRegistrationFailsOnBadJSON(c *C) { |
738 | +func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { |
739 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
740 | buf := make([]byte, 256) |
741 | n, e := r.Body.Read(buf) |
742 | @@ -241,18 +250,19 @@ |
743 | fmt.Fprintln(w, `{`) |
744 | })) |
745 | defer ts.Close() |
746 | - |
747 | - svc := NewPushService(ss.bus, ss.log) |
748 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
749 | - svc.SetRegistrationURL(ts.URL) |
750 | - svc.SetDeviceId("fake-device-id") |
751 | + setup := &PushServiceSetup{ |
752 | + DeviceId: "fake-device-id", |
753 | + RegURL: helpers.ParseURL(ts.URL), |
754 | + AuthGetter: func(string) string { return "tok" }, |
755 | + } |
756 | + svc := NewPushService(ss.bus, setup, ss.log) |
757 | // this'll check (un)quoting, too |
758 | reg, err := svc.register("/an_2dapp_2did", nil, nil) |
759 | c.Check(reg, IsNil) |
760 | c.Check(err, ErrorMatches, "unable to unmarshal register response: .*") |
761 | } |
762 | |
763 | -func (ss *serviceSuite) TestRegistrationFailsOnBadJSONDocument(c *C) { |
764 | +func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { |
765 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
766 | buf := make([]byte, 256) |
767 | n, e := r.Body.Read(buf) |
768 | @@ -265,11 +275,12 @@ |
769 | fmt.Fprintln(w, `{"bananas": "very yes"}`) |
770 | })) |
771 | defer ts.Close() |
772 | - |
773 | - svc := NewPushService(ss.bus, ss.log) |
774 | - svc.SetAuthGetter(func(string) string { return "tok" }) |
775 | - svc.SetRegistrationURL(ts.URL) |
776 | - svc.SetDeviceId("fake-device-id") |
777 | + setup := &PushServiceSetup{ |
778 | + DeviceId: "fake-device-id", |
779 | + RegURL: helpers.ParseURL(ts.URL), |
780 | + AuthGetter: func(string) string { return "tok" }, |
781 | + } |
782 | + svc := NewPushService(ss.bus, setup, ss.log) |
783 | // this'll check (un)quoting, too |
784 | reg, err := svc.register("/an_2dapp_2did", nil, nil) |
785 | c.Check(reg, IsNil) |
786 | |
787 | === modified file 'debian/config.json' |
788 | --- debian/config.json 2014-06-19 21:12:04 +0000 |
789 | +++ debian/config.json 2014-07-03 14:00:43 +0000 |
790 | @@ -1,7 +1,7 @@ |
791 | { |
792 | "auth_helper": "/usr/lib/ubuntu-push-client/signing-helper", |
793 | "session_url": "https://push.ubuntu.com/", |
794 | - "registration_url": "https://push.ubuntu.com/register", |
795 | + "registration_url": "https://push.ubuntu.com", |
796 | "connect_timeout": "20s", |
797 | "exchange_timeout": "30s", |
798 | "hosts_cache_expiry": "12h", |
799 | |
800 | === modified file 'testing/helpers.go' |
801 | --- testing/helpers.go 2014-06-23 12:46:28 +0000 |
802 | +++ testing/helpers.go 2014-07-03 14:00:43 +0000 |
803 | @@ -20,6 +20,7 @@ |
804 | import ( |
805 | "encoding/json" |
806 | "fmt" |
807 | + "net/url" |
808 | "os" |
809 | "path" |
810 | "path/filepath" |
811 | @@ -160,3 +161,12 @@ |
812 | } |
813 | return res |
814 | } |
815 | + |
816 | +// ParseURL parses a URL conveniently. |
817 | +func ParseURL(s string) *url.URL { |
818 | + purl, err := url.Parse(s) |
819 | + if err != nil { |
820 | + panic(err) |
821 | + } |
822 | + return purl |
823 | +} |