Merge lp:~pedronis/ubuntu-push/refactor-service-setup into lp:ubuntu-push

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
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.

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
=== modified file 'client/client.go'
--- client/client.go 2014-07-01 11:55:30 +0000
+++ client/client.go 2014-07-03 14:00:43 +0000
@@ -26,6 +26,7 @@
26 "errors"26 "errors"
27 "fmt"27 "fmt"
28 "io/ioutil"28 "io/ioutil"
29 "net/url"
29 "os"30 "os"
30 "os/exec"31 "os/exec"
31 "strings"32 "strings"
@@ -163,6 +164,19 @@
163 }164 }
164}165}
165166
167// derivePushServiceSetup derives the service setup from the client configuration bits.
168func (client *PushClient) derivePushServiceSetup() (*service.PushServiceSetup, error) {
169 setup := new(service.PushServiceSetup)
170 purl, err := url.Parse(client.config.RegistrationURL)
171 if err != nil {
172 return nil, fmt.Errorf("cannot parse registration url: %v", err)
173 }
174 setup.RegURL = purl
175 setup.DeviceId = client.deviceId
176 setup.AuthGetter = client.getAuthorization
177 return setup, nil
178}
179
166// getAuthorization gets the authorization blob to send to the server180// getAuthorization gets the authorization blob to send to the server
167func (client *PushClient) getAuthorization(url string) string {181func (client *PushClient) getAuthorization(url string) string {
168 client.log.Debugf("getting authorization for %s", url)182 client.log.Debugf("getting authorization for %s", url)
@@ -320,9 +334,7 @@
320 if !client.filterBroadcastNotification(msg) {334 if !client.filterBroadcastNotification(msg) {
321 return nil335 return nil
322 }336 }
323 not_id, err := client.postalService.SendNotification(service.ACTION_ID_BROADCAST,337 not_id, err := client.postalService.InjectBroadcast()
324 "update_manager_icon", "There's an updated system image.",
325 "Tap to open the system updater.")
326 if err != nil {338 if err != nil {
327 client.log.Errorf("showing notification: %s", err)339 client.log.Errorf("showing notification: %s", err)
328 return err340 return err
@@ -338,15 +350,20 @@
338}350}
339351
340// handleClick deals with the user clicking a notification352// handleClick deals with the user clicking a notification
341func (client *PushClient) handleClick(action_id string) error {353func (client *PushClient) handleClick(actionId string) error {
342 // “The string is a stark data structure and everywhere it is passed354 // “The string is a stark data structure and everywhere it is passed
343 // there is much duplication of process. It is a perfect vehicle for355 // there is much duplication of process. It is a perfect vehicle for
344 // hiding information.”356 // hiding information.”
345 //357 //
346 // From ACM's SIGPLAN publication, (September, 1982), Article358 // From ACM's SIGPLAN publication, (September, 1982), Article
347 // "Epigrams in Programming", by Alan J. Perlis of Yale University.359 // "Epigrams in Programming", by Alan J. Perlis of Yale University.
348 url := strings.TrimPrefix(action_id, service.ACTION_ID_SNOWFLAKE)360 url := actionId
349 if len(url) == len(action_id) || len(url) == 0 {361 // XXX: branch for the broadcast notifications
362 if strings.HasPrefix(actionId, service.ACTION_ID_PREFIX) {
363 parts := strings.Split(actionId, "::")
364 url = parts[1]
365 }
366 if len(url) == len(actionId) || len(url) == 0 {
350 // it didn't start with the prefix367 // it didn't start with the prefix
351 return nil368 return nil
352 }369 }
@@ -396,6 +413,10 @@
396}413}
397414
398func (client *PushClient) startService() error {415func (client *PushClient) startService() error {
416 setup, err := client.derivePushServiceSetup()
417 if err != nil {
418 return err
419 }
399 if client.pushServiceEndpoint == nil {420 if client.pushServiceEndpoint == nil {
400 client.pushServiceEndpoint = bus.SessionBus.Endpoint(service.PushServiceBusAddress, client.log)421 client.pushServiceEndpoint = bus.SessionBus.Endpoint(service.PushServiceBusAddress, client.log)
401 }422 }
@@ -403,10 +424,7 @@
403 client.postalServiceEndpoint = bus.SessionBus.Endpoint(service.PostalServiceBusAddress, client.log)424 client.postalServiceEndpoint = bus.SessionBus.Endpoint(service.PostalServiceBusAddress, client.log)
404 }425 }
405426
406 client.pushService = service.NewPushService(client.pushServiceEndpoint, client.log)427 client.pushService = service.NewPushService(client.pushServiceEndpoint, setup, client.log)
407 client.pushService.SetRegistrationURL(client.config.RegistrationURL)
408 client.pushService.SetAuthGetter(client.getAuthorization)
409 client.pushService.SetDeviceId(client.deviceId)
410 if err := client.pushService.Start(); err != nil {428 if err := client.pushService.Start(); err != nil {
411 return err429 return err
412 }430 }
413431
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-07-01 11:55:30 +0000
+++ client/client_test.go 2014-07-03 14:00:43 +0000
@@ -289,7 +289,7 @@
289 // finally compare289 // finally compare
290 conf := cli.deriveSessionConfig(info)290 conf := cli.deriveSessionConfig(info)
291 // compare authGetter by string291 // compare authGetter by string
292 c.Check(fmt.Sprintf("%v", conf.AuthGetter), Equals, fmt.Sprintf("%v", cli.getAuthorization))292 c.Check(fmt.Sprintf("%#v", conf.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization))
293 // and set it to nil293 // and set it to nil
294 conf.AuthGetter = nil294 conf.AuthGetter = nil
295 expected.AuthGetter = nil295 expected.AuthGetter = nil
@@ -297,6 +297,51 @@
297}297}
298298
299/*****************************************************************299/*****************************************************************
300 derivePushServiceSetup tests
301******************************************************************/
302
303func (cs *clientSuite) TestDerivePushServiceSetup(c *C) {
304 cs.writeTestConfig(map[string]interface{}{})
305 cli := NewPushClient(cs.configPath, cs.leveldbPath)
306 err := cli.configure()
307 c.Assert(err, IsNil)
308 cli.deviceId = "zoo"
309 expected := &service.PushServiceSetup{
310 DeviceId: "zoo",
311 AuthGetter: func(string) string { return "" },
312 RegURL: helpers.ParseURL("reg://"),
313 }
314 // sanity check that we are looking at all fields
315 vExpected := reflect.ValueOf(expected).Elem()
316 nf := vExpected.NumField()
317 for i := 0; i < nf; i++ {
318 fv := vExpected.Field(i)
319 // field isn't empty/zero
320 c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name))
321 }
322 // finally compare
323 setup, err := cli.derivePushServiceSetup()
324 c.Assert(err, IsNil)
325 // compare authGetter by string
326 c.Check(fmt.Sprintf("%#v", setup.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization))
327 // and set it to nil
328 setup.AuthGetter = nil
329 expected.AuthGetter = nil
330 c.Check(setup, DeepEquals, expected)
331}
332
333func (cs *clientSuite) TestDerivePushServiceSetupError(c *C) {
334 cs.writeTestConfig(map[string]interface{}{
335 "registration_url": "%gh",
336 })
337 cli := NewPushClient(cs.configPath, cs.leveldbPath)
338 err := cli.configure()
339 c.Assert(err, IsNil)
340 _, err = cli.derivePushServiceSetup()
341 c.Check(err, ErrorMatches, "cannot parse registration url:.*")
342}
343
344/*****************************************************************
300 startService tests345 startService tests
301******************************************************************/346******************************************************************/
302347
@@ -305,7 +350,8 @@
305 "auth_helper": helpers.ScriptAbsPath("dummyauth.sh"),350 "auth_helper": helpers.ScriptAbsPath("dummyauth.sh"),
306 })351 })
307 cli := NewPushClient(cs.configPath, cs.leveldbPath)352 cli := NewPushClient(cs.configPath, cs.leveldbPath)
308 cli.configure()353 err := cli.configure()
354 c.Assert(err, IsNil)
309 cli.log = cs.log355 cli.log = cs.log
310 cli.deviceId = "fake-id"356 cli.deviceId = "fake-id"
311 cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)357 cli.pushServiceEndpoint = testibus.NewTestingEndpoint(condition.Work(true), nil)
@@ -314,8 +360,6 @@
314 c.Check(cli.startService(), IsNil)360 c.Check(cli.startService(), IsNil)
315 c.Assert(cli.pushService, NotNil)361 c.Assert(cli.pushService, NotNil)
316 c.Check(cli.pushService.IsRunning(), Equals, true)362 c.Check(cli.pushService.IsRunning(), Equals, true)
317 c.Check(cli.pushService.GetDeviceId(), Equals, "fake-id")
318 c.Check(cli.pushService.GetRegistrationAuthorization(), Equals, "hello reg://")
319 c.Assert(cli.setupPostalService(), IsNil)363 c.Assert(cli.setupPostalService(), IsNil)
320 c.Assert(cli.startPostalService(), IsNil)364 c.Assert(cli.startPostalService(), IsNil)
321 c.Check(cli.postalService.IsRunning(), Equals, true)365 c.Check(cli.postalService.IsRunning(), Equals, true)
@@ -323,6 +367,17 @@
323 cli.postalService.Stop()367 cli.postalService.Stop()
324}368}
325369
370func (cs *clientSuite) TestStartServiceSetupError(c *C) {
371 cs.writeTestConfig(map[string]interface{}{
372 "registration_url": "%gh",
373 })
374 cli := NewPushClient(cs.configPath, cs.leveldbPath)
375 err := cli.configure()
376 c.Assert(err, IsNil)
377 err = cli.startService()
378 c.Check(err, ErrorMatches, "cannot parse registration url:.*")
379}
380
326func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) {381func (cs *clientSuite) TestStartServiceErrorsOnNilLog(c *C) {
327 cli := NewPushClient(cs.configPath, cs.leveldbPath)382 cli := NewPushClient(cs.configPath, cs.leveldbPath)
328 c.Check(cli.log, IsNil)383 c.Check(cli.log, IsNil)
@@ -712,6 +767,8 @@
712 handleClick tests767 handleClick tests
713******************************************************************/768******************************************************************/
714769
770var ACTION_ID_BROADCAST = service.ACTION_ID_PREFIX + service.SystemUpdateUrl + service.ACTION_ID_SUFFIX
771
715func (cs *clientSuite) TestHandleClick(c *C) {772func (cs *clientSuite) TestHandleClick(c *C) {
716 cli := NewPushClient(cs.configPath, cs.leveldbPath)773 cli := NewPushClient(cs.configPath, cs.leveldbPath)
717 cli.log = cs.log774 cli.log = cs.log
@@ -723,14 +780,14 @@
723 args := testibus.GetCallArgs(endp)780 args := testibus.GetCallArgs(endp)
724 c.Assert(args, HasLen, 0)781 c.Assert(args, HasLen, 0)
725 // check we worked with the right action id782 // check we worked with the right action id
726 c.Check(cli.handleClick(service.ACTION_ID_BROADCAST), IsNil)783 c.Check(cli.handleClick(ACTION_ID_BROADCAST), IsNil)
727 // check we sent the notification784 // check we sent the notification
728 args = testibus.GetCallArgs(endp)785 args = testibus.GetCallArgs(endp)
729 c.Assert(args, HasLen, 1)786 c.Assert(args, HasLen, 1)
730 c.Check(args[0].Member, Equals, "DispatchURL")787 c.Check(args[0].Member, Equals, "DispatchURL")
731 c.Check(args[0].Args, DeepEquals, []interface{}{service.SystemUpdateUrl})788 c.Check(args[0].Args, DeepEquals, []interface{}{service.SystemUpdateUrl})
732 // check we worked with the right action id789 // check we worked with the right action id
733 c.Check(cli.handleClick(service.ACTION_ID_SNOWFLAKE+"foo"), IsNil)790 c.Check(cli.handleClick(service.ACTION_ID_PREFIX+"foo"), IsNil)
734 // check we sent the notification791 // check we sent the notification
735 args = testibus.GetCallArgs(endp)792 args = testibus.GetCallArgs(endp)
736 c.Assert(args, HasLen, 2)793 c.Assert(args, HasLen, 2)
@@ -879,7 +936,7 @@
879 c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$")936 c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$")
880937
881 // * actionsCh to the click handler/url dispatcher938 // * actionsCh to the click handler/url dispatcher
882 aCh <- notifications.RawActionReply{ActionId: service.ACTION_ID_BROADCAST}939 aCh <- notifications.RawActionReply{ActionId: ACTION_ID_BROADCAST}
883 tick()940 tick()
884 uargs := testibus.GetCallArgs(cli.urlDispatcherEndp)941 uargs := testibus.GetCallArgs(cli.urlDispatcherEndp)
885 c.Assert(uargs, HasLen, 1)942 c.Assert(uargs, HasLen, 1)
886943
=== modified file 'client/service/postal.go'
--- client/service/postal.go 2014-07-01 11:55:30 +0000
+++ client/service/postal.go 2014-07-03 14:00:43 +0000
@@ -20,7 +20,6 @@
20 "strings"20 "strings"
2121
22 "code.google.com/p/go-uuid/uuid"22 "code.google.com/p/go-uuid/uuid"
23 "launchpad.net/go-dbus/v1"
2423
25 "launchpad.net/ubuntu-push/bus"24 "launchpad.net/ubuntu-push/bus"
26 "launchpad.net/ubuntu-push/bus/notifications"25 "launchpad.net/ubuntu-push/bus/notifications"
@@ -50,9 +49,9 @@
50)49)
5150
52var (51var (
53 SystemUpdateUrl = "settings:///system/system-update"52 SystemUpdateUrl = "settings:///system/system-update"
54 ACTION_ID_SNOWFLAKE = "::ubuntu-push-client::"53 ACTION_ID_PREFIX = "ubuntu-push-client::"
55 ACTION_ID_BROADCAST = ACTION_ID_SNOWFLAKE + SystemUpdateUrl54 ACTION_ID_SUFFIX = "::0"
56)55)
5756
58// NewPostalService() builds a new service and returns it.57// NewPostalService() builds a new service and returns it.
@@ -90,9 +89,7 @@
90}89}
9190
92func (svc *PostalService) TakeTheBus() (<-chan notifications.RawActionReply, error) {91func (svc *PostalService) TakeTheBus() (<-chan notifications.RawActionReply, error) {
93 iniCh := make(chan uint32)92 util.NewAutoRedialer(svc.notificationsEndp).Redial()
94 go func() { iniCh <- util.NewAutoRedialer(svc.notificationsEndp).Redial() }()
95 <-iniCh
96 actionsCh, err := notifications.Raw(svc.notificationsEndp, svc.Log).WatchActions()93 actionsCh, err := notifications.Raw(svc.notificationsEndp, svc.Log).WatchActions()
97 return actionsCh, err94 return actionsCh, err
98}95}
@@ -164,18 +161,14 @@
164 return err161 return err
165}162}
166163
167func (svc *PostalService) SendNotification(action_id, icon, summary, body string) (uint32, error) {164func (svc *PostalService) InjectBroadcast() (uint32, error) {
168 a := []string{action_id, "Switch to app"} // action value not visible on the phone165 // XXX: call a helper?
169 h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}}166 // XXX: Present force us to send the url as the notificationId
170 nots := notifications.Raw(svc.notificationsEndp, svc.Log)167 icon := "update_manager_icon"
171 return nots.Notify(168 summary := "There's an updated system image."
172 "ubuntu-push-client", // app name169 body := "Tap to open the system updater."
173 uint32(0), // id170 actions := []string{"Switch to app"} // action value not visible on the phone
174 icon, // icon171 card := &launch_helper.Card{Icon: icon, Summary: summary, Body: body, Actions: actions, Popup: true}
175 summary, // summary172 output := &launch_helper.HelperOutput{[]byte(""), &launch_helper.Notification{Card: card}}
176 body, // body173 return 0, svc.msgHandler("ubuntu-push-client", SystemUpdateUrl, output)
177 a, // actions
178 h, // hints
179 int32(10*1000), // timeout (ms)
180 )
181}174}
182175
=== modified file 'client/service/postal_test.go'
--- client/service/postal_test.go 2014-07-01 11:55:30 +0000
+++ client/service/postal_test.go 2014-07-03 14:00:43 +0000
@@ -157,6 +157,38 @@
157}157}
158158
159//159//
160// Injection (Broadcast) tests
161
162func (ss *postalSuite) TestInjectBroadcast(c *C) {
163 bus := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
164 svc := NewPostalService(ss.bus, bus, ss.log)
165 //svc.msgHandler = nil
166 rvs, err := svc.InjectBroadcast()
167 c.Assert(err, IsNil)
168 c.Check(rvs, Equals, uint32(0))
169 c.Assert(err, IsNil)
170 // and check it fired the right signal (twice)
171 callArgs := testibus.GetCallArgs(bus)
172 c.Assert(callArgs, HasLen, 1)
173 c.Check(callArgs[0].Member, Equals, "Notify")
174 c.Check(callArgs[0].Args[0:6], DeepEquals, []interface{}{"ubuntu-push-client", uint32(0), "update_manager_icon",
175 "There's an updated system image.", "Tap to open the system updater.",
176 []string{"ubuntu-push-client::settings:///system/system-update::0", "Switch to app"}})
177 // TODO: check the map in callArgs?
178 // c.Check(callArgs[0].Args[7]["x-canonical-secondary-icon"], NotNil)
179 // c.Check(callArgs[0].Args[7]["x-canonical-snap-decisions"], NotNil)
180}
181
182func (ss *postalSuite) TestInjectBroadcastFails(c *C) {
183 bus := testibus.NewTestingEndpoint(condition.Work(true),
184 condition.Work(false))
185 svc := NewPostalService(ss.bus, bus, ss.log)
186 svc.SetMessageHandler(func(string, string, *launch_helper.HelperOutput) error { return errors.New("fail") })
187 _, err := svc.InjectBroadcast()
188 c.Check(err, NotNil)
189}
190
191//
160// Notifications tests192// Notifications tests
161func (ss *postalSuite) TestNotificationsWorks(c *C) {193func (ss *postalSuite) TestNotificationsWorks(c *C) {
162 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)194 svc := NewPostalService(ss.bus, ss.notifBus, ss.log)
163195
=== modified file 'client/service/service.go'
--- client/service/service.go 2014-07-01 11:55:30 +0000
+++ client/service/service.go 2014-07-03 14:00:43 +0000
@@ -23,6 +23,7 @@
23 "fmt"23 "fmt"
24 "io/ioutil"24 "io/ioutil"
25 "net/http"25 "net/http"
26 "net/url"
26 "os"27 "os"
27 "strings"28 "strings"
2829
@@ -32,10 +33,17 @@
32 "launchpad.net/ubuntu-push/nih"33 "launchpad.net/ubuntu-push/nih"
33)34)
3435
36// PushServiceSetup encapsulates the params for setting up a PushService.
37type PushServiceSetup struct {
38 RegURL *url.URL
39 DeviceId string
40 AuthGetter func(string) string
41}
42
35// PushService is the dbus api43// PushService is the dbus api
36type PushService struct {44type PushService struct {
37 DBusService45 DBusService
38 regURL string46 regURL *url.URL
39 deviceId string47 deviceId string
40 authGetter func(string) string48 authGetter func(string) string
41 httpCli http13.Client49 httpCli http13.Client
@@ -50,59 +58,28 @@
50)58)
5159
52// NewPushService() builds a new service and returns it.60// NewPushService() builds a new service and returns it.
53func NewPushService(bus bus.Endpoint, log logger.Logger) *PushService {61func NewPushService(bus bus.Endpoint, setup *PushServiceSetup, log logger.Logger) *PushService {
54 var svc = &PushService{}62 var svc = &PushService{}
55 svc.Log = log63 svc.Log = log
56 svc.Bus = bus64 svc.Bus = bus
65 svc.regURL = setup.RegURL
66 svc.deviceId = setup.DeviceId
67 svc.authGetter = setup.AuthGetter
57 return svc68 return svc
58}69}
5970
60// SetRegistrationURL() sets the registration url for the service71// getAuthorization() returns the URL and the authorization header for
61func (svc *PushService) SetRegistrationURL(url string) {72// POSTing to the registration HTTP endpoint for op
62 svc.lock.Lock()73func (svc *PushService) getAuthorization(op string) (string, string) {
63 defer svc.lock.Unlock()74 if svc.authGetter == nil || svc.regURL == nil {
64 svc.regURL = url75 return "", ""
65}76 }
6677 purl, err := svc.regURL.Parse(op)
67// SetAuthGetter() sets the authorization getter for the service78 if err != nil {
68func (svc *PushService) SetAuthGetter(authGetter func(string) string) {79 panic("op to getAuthorization was invalid")
69 svc.lock.Lock()80 }
70 defer svc.lock.Unlock()81 url := purl.String()
71 svc.authGetter = authGetter82 return url, svc.authGetter(url)
72}
73
74// getRegistrationAuthorization() returns the authorization header for
75// POSTing to the registration HTTP endpoint
76//
77// (this is for calling with the lock held)
78func (svc *PushService) getRegistrationAuthorization() string {
79 if svc.authGetter != nil && svc.regURL != "" {
80 return svc.authGetter(svc.regURL)
81 } else {
82 return ""
83 }
84}
85
86// GetRegistrationAuthorization() returns the authorization header for
87// POSTing to the registration HTTP endpoint
88func (svc *PushService) GetRegistrationAuthorization() string {
89 svc.lock.RLock()
90 defer svc.lock.RUnlock()
91 return svc.getRegistrationAuthorization()
92}
93
94// SetDeviceId() sets the device id
95func (svc *PushService) SetDeviceId(deviceId string) {
96 svc.lock.Lock()
97 defer svc.lock.Unlock()
98 svc.deviceId = deviceId
99}
100
101// GetDeviceId() returns the device id
102func (svc *PushService) GetDeviceId() string {
103 svc.lock.RLock()
104 defer svc.lock.RUnlock()
105 return svc.deviceId
106}83}
10784
108func (svc *PushService) Start() error {85func (svc *PushService) Start() error {
@@ -130,32 +107,21 @@
130 Message string `json:"message"` //107 Message string `json:"message"` //
131}108}
132109
133func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) {110func (svc *PushService) manageReg(op, appId string) (*registrationReply, error) {
134 svc.lock.RLock()111 req_body, err := json.Marshal(registrationRequest{svc.deviceId, appId})
135 defer svc.lock.RUnlock()
136 if len(args) != 0 {
137 return nil, BadArgCount
138 }
139 raw_appname := path[strings.LastIndex(path, "/")+1:]
140 appname := string(nih.Unquote([]byte(raw_appname)))
141
142 rv := os.Getenv("PUSH_REG_" + raw_appname)
143 if rv != "" {
144 return []interface{}{rv}, nil
145 }
146
147 req_body, err := json.Marshal(registrationRequest{svc.deviceId, appname})
148 if err != nil {112 if err != nil {
149 return nil, fmt.Errorf("unable to marshal register request body: %v", err)113 return nil, fmt.Errorf("unable to marshal register request body: %v", err)
150 }114 }
151 req, err := http13.NewRequest("POST", svc.regURL, bytes.NewReader(req_body))115
152 if err != nil {116 url, auth := svc.getAuthorization(op)
153 return nil, fmt.Errorf("unable to build register request: %v", err)
154 }
155 auth := svc.getRegistrationAuthorization()
156 if auth == "" {117 if auth == "" {
157 return nil, BadAuth118 return nil, BadAuth
158 }119 }
120
121 req, err := http13.NewRequest("POST", url, bytes.NewReader(req_body))
122 if err != nil {
123 panic(fmt.Errorf("unable to build register request: %v", err))
124 }
159 req.Header.Add("Authorization", auth)125 req.Header.Add("Authorization", auth)
160 req.Header.Add("Content-Type", "application/json")126 req.Header.Add("Content-Type", "application/json")
161127
@@ -188,6 +154,26 @@
188 return nil, fmt.Errorf("unable to unmarshal register response: %v", err)154 return nil, fmt.Errorf("unable to unmarshal register response: %v", err)
189 }155 }
190156
157 return &reply, nil
158}
159
160func (svc *PushService) register(path string, args, _ []interface{}) ([]interface{}, error) {
161 if len(args) != 0 {
162 return nil, BadArgCount
163 }
164 raw_appname := path[strings.LastIndex(path, "/")+1:]
165 appname := string(nih.Unquote([]byte(raw_appname)))
166
167 rv := os.Getenv("PUSH_REG_" + raw_appname)
168 if rv != "" {
169 return []interface{}{rv}, nil
170 }
171
172 reply, err := svc.manageReg("/register", appname)
173 if err != nil {
174 return nil, err
175 }
176
191 if !reply.Ok || reply.Token == "" {177 if !reply.Ok || reply.Token == "" {
192 svc.Log.Errorf("Unexpected response: %#v", reply)178 svc.Log.Errorf("Unexpected response: %#v", reply)
193 return nil, BadToken179 return nil, BadToken
194180
=== modified file 'client/service/service_test.go'
--- client/service/service_test.go 2014-06-20 12:33:03 +0000
+++ client/service/service_test.go 2014-07-03 14:00:43 +0000
@@ -47,8 +47,25 @@
47 ss.bus = testibus.NewTestingEndpoint(condition.Work(true), nil)47 ss.bus = testibus.NewTestingEndpoint(condition.Work(true), nil)
48}48}
4949
50var testSetup = &PushServiceSetup{}
51
52func (ss *serviceSuite) TestBuild(c *C) {
53 setup := &PushServiceSetup{
54 RegURL: helpers.ParseURL("http://reg"),
55 DeviceId: "FOO",
56 AuthGetter: func(s string) string {
57 return ""
58 },
59 }
60 svc := NewPushService(ss.bus, setup, ss.log)
61 c.Check(svc.Bus, Equals, ss.bus)
62 c.Check(svc.regURL, DeepEquals, helpers.ParseURL("http://reg"))
63 c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", setup.AuthGetter))
64 // ...
65}
66
50func (ss *serviceSuite) TestStart(c *C) {67func (ss *serviceSuite) TestStart(c *C) {
51 svc := NewPushService(ss.bus, ss.log)68 svc := NewPushService(ss.bus, testSetup, ss.log)
52 c.Check(svc.IsRunning(), Equals, false)69 c.Check(svc.IsRunning(), Equals, false)
53 c.Check(svc.Start(), IsNil)70 c.Check(svc.Start(), IsNil)
54 c.Check(svc.IsRunning(), Equals, true)71 c.Check(svc.IsRunning(), Equals, true)
@@ -56,31 +73,31 @@
56}73}
5774
58func (ss *serviceSuite) TestStartTwice(c *C) {75func (ss *serviceSuite) TestStartTwice(c *C) {
59 svc := NewPushService(ss.bus, ss.log)76 svc := NewPushService(ss.bus, testSetup, ss.log)
60 c.Check(svc.Start(), IsNil)77 c.Check(svc.Start(), IsNil)
61 c.Check(svc.Start(), Equals, AlreadyStarted)78 c.Check(svc.Start(), Equals, AlreadyStarted)
62 svc.Stop()79 svc.Stop()
63}80}
6481
65func (ss *serviceSuite) TestStartNoLog(c *C) {82func (ss *serviceSuite) TestStartNoLog(c *C) {
66 svc := NewPushService(ss.bus, nil)83 svc := NewPushService(ss.bus, testSetup, nil)
67 c.Check(svc.Start(), Equals, NotConfigured)84 c.Check(svc.Start(), Equals, NotConfigured)
68}85}
6986
70func (ss *serviceSuite) TestStartNoBus(c *C) {87func (ss *serviceSuite) TestStartNoBus(c *C) {
71 svc := NewPushService(nil, ss.log)88 svc := NewPushService(nil, testSetup, ss.log)
72 c.Check(svc.Start(), Equals, NotConfigured)89 c.Check(svc.Start(), Equals, NotConfigured)
73}90}
7491
75func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) {92func (ss *serviceSuite) TestStartFailsOnBusDialFailure(c *C) {
76 bus := testibus.NewTestingEndpoint(condition.Work(false), nil)93 bus := testibus.NewTestingEndpoint(condition.Work(false), nil)
77 svc := NewPushService(bus, ss.log)94 svc := NewPushService(bus, testSetup, ss.log)
78 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)95 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)
79 svc.Stop()96 svc.Stop()
80}97}
8198
82func (ss *serviceSuite) TestStartGrabsName(c *C) {99func (ss *serviceSuite) TestStartGrabsName(c *C) {
83 svc := NewPushService(ss.bus, ss.log)100 svc := NewPushService(ss.bus, testSetup, ss.log)
84 c.Assert(svc.Start(), IsNil)101 c.Assert(svc.Start(), IsNil)
85 callArgs := testibus.GetCallArgs(ss.bus)102 callArgs := testibus.GetCallArgs(ss.bus)
86 defer svc.Stop()103 defer svc.Stop()
@@ -89,7 +106,7 @@
89}106}
90107
91func (ss *serviceSuite) TestStopClosesBus(c *C) {108func (ss *serviceSuite) TestStopClosesBus(c *C) {
92 svc := NewPushService(ss.bus, ss.log)109 svc := NewPushService(ss.bus, testSetup, ss.log)
93 c.Assert(svc.Start(), IsNil)110 c.Assert(svc.Start(), IsNil)
94 svc.Stop()111 svc.Stop()
95 callArgs := testibus.GetCallArgs(ss.bus)112 callArgs := testibus.GetCallArgs(ss.bus)
@@ -99,35 +116,27 @@
99116
100// registration tests117// registration tests
101118
102func (ss *serviceSuite) TestSetRegURLWorks(c *C) {
103 svc := NewPushService(ss.bus, ss.log)
104 c.Check(svc.regURL, Equals, "")
105 svc.SetRegistrationURL("xyzzy://")
106 c.Check(svc.regURL, Equals, "xyzzy://")
107}
108
109func (ss *serviceSuite) TestSetAuthGetterWorks(c *C) {
110 svc := NewPushService(ss.bus, ss.log)
111 c.Check(svc.authGetter, IsNil)
112 f := func(string) string { return "" }
113 svc.SetAuthGetter(f)
114 c.Check(fmt.Sprintf("%#v", svc.authGetter), Equals, fmt.Sprintf("%#v", f))
115}
116
117func (ss *serviceSuite) TestGetRegAuthWorks(c *C) {119func (ss *serviceSuite) TestGetRegAuthWorks(c *C) {
118 svc := NewPushService(ss.bus, ss.log)
119 svc.SetRegistrationURL("xyzzy://")
120 ch := make(chan string, 1)120 ch := make(chan string, 1)
121 f := func(s string) string { ch <- s; return "Auth " + s }121 setup := &PushServiceSetup{
122 svc.SetAuthGetter(f)122 RegURL: helpers.ParseURL("http://foo"),
123 c.Check(svc.getRegistrationAuthorization(), Equals, "Auth xyzzy://")123 AuthGetter: func(s string) string {
124 ch <- s
125 return "Auth " + s
126 },
127 }
128 svc := NewPushService(ss.bus, setup, ss.log)
129 url, auth := svc.getAuthorization("/op")
130 c.Check(auth, Equals, "Auth http://foo/op")
124 c.Assert(len(ch), Equals, 1)131 c.Assert(len(ch), Equals, 1)
125 c.Check(<-ch, Equals, "xyzzy://")132 c.Check(<-ch, Equals, "http://foo/op")
133 c.Check(url, Equals, "http://foo/op")
126}134}
127135
128func (ss *serviceSuite) TestGetRegAuthDoesNotPanic(c *C) {136func (ss *serviceSuite) TestGetRegAuthDoesNotPanic(c *C) {
129 svc := NewPushService(ss.bus, ss.log)137 svc := NewPushService(ss.bus, testSetup, ss.log)
130 c.Check(svc.getRegistrationAuthorization(), Equals, "")138 _, auth := svc.getAuthorization("/op")
139 c.Check(auth, Equals, "")
131}140}
132141
133func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) {142func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) {
@@ -149,11 +158,12 @@
149 fmt.Fprintln(w, `{"ok":true,"token":"blob-of-bytes"}`)158 fmt.Fprintln(w, `{"ok":true,"token":"blob-of-bytes"}`)
150 }))159 }))
151 defer ts.Close()160 defer ts.Close()
152161 setup := &PushServiceSetup{
153 svc := NewPushService(ss.bus, ss.log)162 DeviceId: "fake-device-id",
154 svc.SetAuthGetter(func(string) string { return "tok" })163 RegURL: helpers.ParseURL(ts.URL),
155 svc.SetRegistrationURL(ts.URL)164 AuthGetter: func(string) string { return "tok" },
156 svc.SetDeviceId("fake-device-id")165 }
166 svc := NewPushService(ss.bus, setup, ss.log)
157 // this'll check (un)quoting, too167 // this'll check (un)quoting, too
158 reg, err := svc.register("/an_2dapp_2did", nil, nil)168 reg, err := svc.register("/an_2dapp_2did", nil, nil)
159 c.Assert(err, IsNil)169 c.Assert(err, IsNil)
@@ -175,60 +185,59 @@
175 c.Check(err, IsNil)185 c.Check(err, IsNil)
176}186}
177187
178func (ss *serviceSuite) TestRegistrationFailsOnBadReqURL(c *C) {188func (ss *serviceSuite) TestManageRegFailsOnBadAuth(c *C) {
179 svc := NewPushService(ss.bus, ss.log)
180 svc.SetRegistrationURL("%gh")
181 reg, err := svc.register("thing", nil, nil)
182 c.Check(reg, IsNil)
183 c.Check(err, ErrorMatches, "unable to build register request: .*")
184}
185
186func (ss *serviceSuite) TestRegistrationFailsOnBadAuth(c *C) {
187 svc := NewPushService(ss.bus, ss.log)
188 // ... no auth added189 // ... no auth added
190 svc := NewPushService(ss.bus, testSetup, ss.log)
189 reg, err := svc.register("thing", nil, nil)191 reg, err := svc.register("thing", nil, nil)
190 c.Check(reg, IsNil)192 c.Check(reg, IsNil)
191 c.Check(err, Equals, BadAuth)193 c.Check(err, Equals, BadAuth)
192}194}
193195
194func (ss *serviceSuite) TestRegistrationFailsOnNoServer(c *C) {196func (ss *serviceSuite) TestManageRegFailsOnNoServer(c *C) {
195 svc := NewPushService(ss.bus, ss.log)197 setup := &PushServiceSetup{
196 svc.SetRegistrationURL("xyzzy://")198 DeviceId: "fake-device-id",
197 svc.SetAuthGetter(func(string) string { return "tok" })199 RegURL: helpers.ParseURL("xyzzy://"),
200 AuthGetter: func(string) string { return "tok" },
201 }
202 svc := NewPushService(ss.bus, setup, ss.log)
198 reg, err := svc.register("thing", nil, nil)203 reg, err := svc.register("thing", nil, nil)
199 c.Check(reg, IsNil)204 c.Check(reg, IsNil)
200 c.Check(err, ErrorMatches, "unable to request registration: .*")205 c.Check(err, ErrorMatches, "unable to request registration: .*")
201}206}
202207
203func (ss *serviceSuite) TestRegistrationFailsOn40x(c *C) {208func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) {
204 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {209 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
205 http.Error(w, "I'm a teapot", 418)210 http.Error(w, "I'm a teapot", 418)
206 }))211 }))
207 defer ts.Close()212 defer ts.Close()
208213 setup := &PushServiceSetup{
209 svc := NewPushService(ss.bus, ss.log)214 DeviceId: "fake-device-id",
210 svc.SetAuthGetter(func(string) string { return "tok" })215 RegURL: helpers.ParseURL(ts.URL),
211 svc.SetRegistrationURL(ts.URL)216 AuthGetter: func(string) string { return "tok" },
217 }
218 svc := NewPushService(ss.bus, setup, ss.log)
212 reg, err := svc.register("/thing", nil, nil)219 reg, err := svc.register("/thing", nil, nil)
213 c.Check(err, Equals, BadRequest)220 c.Check(err, Equals, BadRequest)
214 c.Check(reg, IsNil)221 c.Check(reg, IsNil)
215}222}
216223
217func (ss *serviceSuite) TestRegistrationFailsOn50x(c *C) {224func (ss *serviceSuite) TestManageRegFailsOn50x(c *C) {
218 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {225 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
219 http.Error(w, "Not implemented", 501)226 http.Error(w, "Not implemented", 501)
220 }))227 }))
221 defer ts.Close()228 defer ts.Close()
222229 setup := &PushServiceSetup{
223 svc := NewPushService(ss.bus, ss.log)230 DeviceId: "fake-device-id",
224 svc.SetAuthGetter(func(string) string { return "tok" })231 RegURL: helpers.ParseURL(ts.URL),
225 svc.SetRegistrationURL(ts.URL)232 AuthGetter: func(string) string { return "tok" },
233 }
234 svc := NewPushService(ss.bus, setup, ss.log)
226 reg, err := svc.register("/thing", nil, nil)235 reg, err := svc.register("/thing", nil, nil)
227 c.Check(err, Equals, BadServer)236 c.Check(err, Equals, BadServer)
228 c.Check(reg, IsNil)237 c.Check(reg, IsNil)
229}238}
230239
231func (ss *serviceSuite) TestRegistrationFailsOnBadJSON(c *C) {240func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) {
232 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {241 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
233 buf := make([]byte, 256)242 buf := make([]byte, 256)
234 n, e := r.Body.Read(buf)243 n, e := r.Body.Read(buf)
@@ -241,18 +250,19 @@
241 fmt.Fprintln(w, `{`)250 fmt.Fprintln(w, `{`)
242 }))251 }))
243 defer ts.Close()252 defer ts.Close()
244253 setup := &PushServiceSetup{
245 svc := NewPushService(ss.bus, ss.log)254 DeviceId: "fake-device-id",
246 svc.SetAuthGetter(func(string) string { return "tok" })255 RegURL: helpers.ParseURL(ts.URL),
247 svc.SetRegistrationURL(ts.URL)256 AuthGetter: func(string) string { return "tok" },
248 svc.SetDeviceId("fake-device-id")257 }
258 svc := NewPushService(ss.bus, setup, ss.log)
249 // this'll check (un)quoting, too259 // this'll check (un)quoting, too
250 reg, err := svc.register("/an_2dapp_2did", nil, nil)260 reg, err := svc.register("/an_2dapp_2did", nil, nil)
251 c.Check(reg, IsNil)261 c.Check(reg, IsNil)
252 c.Check(err, ErrorMatches, "unable to unmarshal register response: .*")262 c.Check(err, ErrorMatches, "unable to unmarshal register response: .*")
253}263}
254264
255func (ss *serviceSuite) TestRegistrationFailsOnBadJSONDocument(c *C) {265func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) {
256 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {266 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
257 buf := make([]byte, 256)267 buf := make([]byte, 256)
258 n, e := r.Body.Read(buf)268 n, e := r.Body.Read(buf)
@@ -265,11 +275,12 @@
265 fmt.Fprintln(w, `{"bananas": "very yes"}`)275 fmt.Fprintln(w, `{"bananas": "very yes"}`)
266 }))276 }))
267 defer ts.Close()277 defer ts.Close()
268278 setup := &PushServiceSetup{
269 svc := NewPushService(ss.bus, ss.log)279 DeviceId: "fake-device-id",
270 svc.SetAuthGetter(func(string) string { return "tok" })280 RegURL: helpers.ParseURL(ts.URL),
271 svc.SetRegistrationURL(ts.URL)281 AuthGetter: func(string) string { return "tok" },
272 svc.SetDeviceId("fake-device-id")282 }
283 svc := NewPushService(ss.bus, setup, ss.log)
273 // this'll check (un)quoting, too284 // this'll check (un)quoting, too
274 reg, err := svc.register("/an_2dapp_2did", nil, nil)285 reg, err := svc.register("/an_2dapp_2did", nil, nil)
275 c.Check(reg, IsNil)286 c.Check(reg, IsNil)
276287
=== modified file 'debian/config.json'
--- debian/config.json 2014-06-19 21:12:04 +0000
+++ debian/config.json 2014-07-03 14:00:43 +0000
@@ -1,7 +1,7 @@
1{1{
2 "auth_helper": "/usr/lib/ubuntu-push-client/signing-helper",2 "auth_helper": "/usr/lib/ubuntu-push-client/signing-helper",
3 "session_url": "https://push.ubuntu.com/",3 "session_url": "https://push.ubuntu.com/",
4 "registration_url": "https://push.ubuntu.com/register",4 "registration_url": "https://push.ubuntu.com",
5 "connect_timeout": "20s",5 "connect_timeout": "20s",
6 "exchange_timeout": "30s",6 "exchange_timeout": "30s",
7 "hosts_cache_expiry": "12h",7 "hosts_cache_expiry": "12h",
88
=== modified file 'testing/helpers.go'
--- testing/helpers.go 2014-06-23 12:46:28 +0000
+++ testing/helpers.go 2014-07-03 14:00:43 +0000
@@ -20,6 +20,7 @@
20import (20import (
21 "encoding/json"21 "encoding/json"
22 "fmt"22 "fmt"
23 "net/url"
23 "os"24 "os"
24 "path"25 "path"
25 "path/filepath"26 "path/filepath"
@@ -160,3 +161,12 @@
160 }161 }
161 return res162 return res
162}163}
164
165// ParseURL parses a URL conveniently.
166func ParseURL(s string) *url.URL {
167 purl, err := url.Parse(s)
168 if err != nil {
169 panic(err)
170 }
171 return purl
172}

Subscribers

People subscribed via source and target branches