WebAPI/SimplePush: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
Line 67: Line 67:
   var reqEmail = navigator.pushNotifications.register();
   var reqEmail = navigator.pushNotifications.register();
   reqEmail.onsuccess = function(e) {
   reqEmail.onsuccess = function(e) {
     emailEndpoint = e.result.pushEndpoint;
     emailEndpoint = e.target.result.pushEndpoint;
     storeOnAppServer("email", emailEndpoint); <i>// This is the "Hand wavey" way that the app  
     storeOnAppServer("email", emailEndpoint); <i>// This is the "Hand wavey" way that the app  
                                                 // sends the endPoint back to the server</i>
                                                 // sends the endPoint back to the server</i>
Line 75: Line 75:
   var reqIm = navigator.pushNotifications.register();
   var reqIm = navigator.pushNotifications.register();
   reqIm.onsuccess = function(e) {
   reqIm.onsuccess = function(e) {
     imEndpoint = e.result.pushEndpoint;
     imEndpoint = e.target.result.pushEndpoint;
     storeOnAppServer("im", imEndpoint);
     storeOnAppServer("im", imEndpoint);
   }
   }

Revision as of 17:36, 5 February 2013

You're a service. Getting clients to talk to you is pretty easy. All you have to do is sit there and wait for them to call. But, what if you want to tell your clients that there's something interesting for them?

SimplePush is a very low cost service that let's servers tell clients "Hey, there's something interesting over here! You ought to do something about that."

Definitions

Before we get into details, it's important that we have a good lexicon.

App
The remote receiver of the SimplePush Notification.
The app is what requests things get established, gets pinged when something interesting happens, and connects back to the AppServer.
AppServer
The third party publisher of update notices.
This is, more than likely, where all the real work of your App is done. It's what has the mail, or the articles, or where someone whacks serpents with farm implements for slices of virtual dessert.
UserAgent
The client acting on behalf of the user and consumer of update notices
This is what handles talking back to the PushServer and wakes up the App when there's something of interest. For example: Firefox Desktop, Firefox OS (a service runs in the background)
PushServer
The server managing interactions between AppServers and the UserAgent.
This is what the AppServer is talking to when it wants to tickle the client.
Endpoint
Unique URL to be used by the AppServer to initiate a response from the App.
This is generated by SimplePush and sent to the App. The App will need to relay this to the AppServer.


The following are SimplePush hoo-doo things that are probably not important to you. Here, look at this device while I put on my sunglasses.

Channel
The flow of information from AppServer through PushServer to UserAgent.
ChannelID
Unique identifier for a Channel. Generated by UserAgent for a particular application.
UAID
A globally unique UserAgent ID


UserAgent and AppServer will use a RESTful API to interact with the Push Server.

Use Cases

An App wishes to be notified when new email arrives. A User installs an App on a mobile device which notifies her when someone adds a note to her bulletin board. The App calls the Registration function, which returns an EndPoint. The App sends the EndPoint to the AppServer using the magic of "Not-Part-Of-This-Protocol". The AppServer stores the EndPoint in the User's information. When a new note is added, the AppServer fetches the EndPoint and PUTs a new version value to it. This alerts SimplePush, which wakes the App and sends a "push" event. This causes the App to refresh it's messages (again using the magical "Not-Part-Of-This-Protocol" system), and User gets a screen full of adorable kittens.

An AppServer wishes to notify Apps of an update. Since a server doesn't want to be deluged by millions of pings from devices every hour, the developers wisely decide to opt for a Push mechanism. Much like the other example, an App registers with SimplePush, gets an EndPoint which it relays to AppServer. AppServer then PUTs a '000' version to the EndPoint which triggers a "push" event for the App, which silently acknowledges that all is well. At some later time, AppServer PUTs '001' to the EndPoint which SimplePush relays to App which then updates itself using "Not-Part-Of-This-Protocol-Either".

Messy Details

Client Side API

Apps will need to add the following to their manifests. See our Web App Development Guide for what this means and how to use it.

App manifest

{
  ...
  "messages": [
    {"push": "/view_to_launch.html"}
    {"push-register": "/view_to_launch.html"}
  ]
} 

JavaScript

We've tried to make the client code as simple as possible. While the following may not be what you use, it shows the general idea.

var emailEndpoint, imEndpoint;

function setupAppRegistrations() {
 // Issue a register() call
 // to register to listen for a notification,
 // you simply call push.register
 // Here, we'll register a channel for "email" updates. Channels can be for anything the app would like.
 var reqEmail = navigator.pushNotifications.register();
 reqEmail.onsuccess = function(e) {
   emailEndpoint = e.target.result.pushEndpoint;
   storeOnAppServer("email", emailEndpoint); // This is the "Hand wavey" way that the app 
                                                // sends the endPoint back to the server
 }

 // We'll also register a second channel for "im", because we're social and all about the socialists. Or something.
 var reqIm = navigator.pushNotifications.register();
 reqIm.onsuccess = function(e) {
   imEndpoint = e.target.result.pushEndpoint;
   storeOnAppServer("im", imEndpoint);
 }
}

// Once we've registered, the AppServer can send version pings to the EndPoint.
// This will trigger a 'push' message to be sent to this handler.
navigator.mozSetMessageHandler('push', {
  handleMessage: function(e) {
    if (e.target.pushEndpoint == emailEndpoint)   // Yay! New Email! Steve and blue can dance!
      getNewEmailMessagesFromAppServer(e.target.version);
    else if (e.target.pushEndpoint == imEndpoint) // Yay! An IM awaits. I wonder if it's Sam's IM?
      getNewChatMessagesFromAppServer(e.target.version);
  }
});

// The user has logged in, now's a good time to register the channels
AppFramework.addEventListener('user-login', function() {
  setupAppRegistrations();
});

// to unregister, you simply call..
AppFramework.addEventListener('user-logout', function() {
  navigator.pushNotifications.unregister(emailEndpoint);
  navigator.pushNotifications.unregister(imEndpoint);
});

// error recovery mechanism
// will be called very rarely, but application
// should register again when it is called
navigator.mozSetMessageHandler('push-register', {
  handleMessage: function(e) {
    if (AppFramework.isUserLoggedIn) {
      setupAppRegistrations();
    }
    else {
      // since user isn't logged in, when he logs in
      // registrations will be setup due to event handler
    }
  }
});

Interface

partial interface Navigator {
  PushNotification pushNotification;
};

interface PushNotification { 

  // registers to receive push notifications for a given topic
  // DOMRequest.result is a PushRegistration in case of success
  DOMRequest register();

  // registers to stop receiving push notifications for a given topic 
  // DOMRequest.result is a PushRegistration in case of success
  DOMRequest unregister(DOMString pushEndpoint);

  // returns the list of all push registrations for this origin
  PushRegistration[] registrations();
};

interface PushRegistration {
  // this is the URL to which push notifications from the AppServer
  // must be sent to.
  // This is how the AppServer alerts the UserAgent that new data exists.
  DOMString pushEndpoint;

  // undefined when obtained from register().onsuccess.
  // contains version when obtained from PushEvent
  DOMString version;
};

interface PushEvent : Event {
  PushRegistration target;
}

Notes

The current spec works for apps. Ideally it should work for web pages loaded in browsers as well. Current issues blocking this:

  • mozSetMessageHandler indexes by app manifest. Web pages don't have app manifests.
  • The lifetime of registrations for web pages needs specification.
  • Every instance of a web page should be treated differently. So the same URL in two tabs should have different channels (and ask permission?). Again geolocation, desktop-notification and others may provide clues)
  • In the case of the b2g browser, the browser is an app and so mozSetMessageHandler gets its manifest, and not access to the pages. We might want to bypass the browser and plug directly into the mozbrowseriframe of each tab. Again within each tab, user might navigate away from page, which may need invalidation.

UI

  • During app installation, the user agent should notify the user that the app uses push notifications, and possibly deny permission.
  • Web pages using push notification should show a doorhangar notification in the same way (look at geolocation to see how this is done).

Server API

ServerAPI has the details of how the ServerAPI works.

Chances are, all you want is to send a new version update.

The simplest way to do that is to call

POST endpoint
version=newversion

Where endpoint is the value that was returned during App's registration.