WebAPI/SimplePush
Client Side API
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(ChannelID channelID); // returns the list of all push registrations for this origin PushRegistration[] registrations(); }; interface PushRegistration { // this is the URL to which push notifications from the web application // must be sent to. // application should send this to the application server DOMString pushEndpoint; // undefined when obtained from register().onsuccess. // contains version when obtained from PushEvent DOMString version; }; interface PushEvent : Event { PushRegistration target; }
Client Example
App manifest
{ ... "messages": [ {"push": "/view_to_launch.html"} {"push-register": "/view_to_launch.html"} ] }
JavaScript
var emailEndpoint, imEndpoint; // all notifications will be sent to the system message handler. // which is up to the U/A to implement // apps can do something like // This handler may be triggered *immediately* after mozSetMessageHandler is called // so applications should ensure they are ready (UI created, state loaded etc.) navigator.mozSetMessageHandler('push-register', { handleMessage: function(e) { // (re)issue register() calls // to register to listen for a notification, // you simply call push.register var reqEmail = navigator.pushNotifications.register(); reqEmail.onsuccess = function(e) { emailEndpoint = e.result.pushEndpoint; storeOnAppServer("email", emailEndpoint); } var reqIm = navigator.pushNotifications.register(); reqIm.onsuccess = function(e) { imEndpoint = e.result.pushEndpoint; storeOnAppServer("im", imEndpoint); } } }); navigator.mozSetMessageHandler('push', { handleMessage: function(e) { e.target.pushEndpoint == This is the topic that is being observed e.target.version == This is the current version for this topic } }); // to unregister, you simply call.. AppFramework.addEventListener('logout', function() { navigator.pushNotifications.unregister(emailEndpoint); navigator.pushNotifications.unregister(imEndpoint); });
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
Definitions
- UserAgent
- The client acting on behalf of the user and consumer of update notices
- UAID
- A server generated, globally unique UserAgent ID
- PushServer
- The server managing interactions between AppServers and the UserAgent
- AppServer
- The third party publisher of update notices
- Channel
- The flow of information from AppServer through PushServer to UserAgent
- ChannelID
- Unique identifier for a Channel. Generated by UserAgent for a particular application.
- Endpoint
- Unique URL comprising the ChannelID. Generated by PushServer and sent to application.
UserAgent and AppServer will use a RESTful API to interact with the Push Server.
API
- Requests from UserAgent to PushServer MUST provide a PushServer Generated UserAgentID (UAID) as the "X-UserAgent-ID" header.
- UserAgent requests may include a /{network} prefix to indicate a request may be handled by a third party network (e.g.
GET http:host/foonet/update
will return update requests for elements provided by the "foonet" sub-provider. TODO: The semantics of this are woefully lacking in the current state of the protocol - Calls will return standard HTTP status headers.
- Calls will return status 200 (unless otherwise noted below).
- On Errors, calls will return a proper error code, and where permitted, a standard JSON block containing information regarding the error. The contents of this block are not intended for end user display and have yet to be finalized.
GET /v1/register/<channelID>
Request a new endpoint.
If the X-UserAgent-ID is not provided, the UserAgent is presumed to be new and a new UAID will be generated for the UserAgent.
The PushServer SHOULD:
- Ensure that the channelID is compliant with any formatting requirements the PushServer may have
- Ensure that the channelID is unique for the UAID.
Arguments
No additional arguments are required.
Returns
{ "channelID": "<channelID provided by UA>", "pushEndpoint": "http://pushserver.provider.tld/path/to/notify" "uaid": "UserAgent ID" (optional) }
NOTE: The uaid is either the X-UserAgent-ID value echoed back, or a new X-UserAgent-ID value to be used for all subsequent calls if no X-UserAgent-ID was present.
pushEndpoint may incorporate channelID to simplify implementation and reduce state storage at server.
Errors
- Invalid Channel ID
- Duplicate Channel ID
Example
GET http://push.services.mozilla.org/v1/register/foo1234 --- {"channelID": "foo1234", "pushEndpoint": "http://push5.services.mozilla.org/v1/update/foo1234", "uaid": "bar5678"}
DELETE /v1/<ChannelID>
Delete an associated ChannelID. Subsequent calls to this ChannelID will result in a 404 status.
NOTE: the X-UserAgent-ID Header must be present and match the one associated with this ChannelID
Arguments
There are no arguments for this call.
Returns
On success, this returns an empty JSON object
{}
Errors
- Not Registered
- Invalid UAID
Example
DELETE http://push.m.o/v1/foo1234 --- {}
GET /v1/update
Return a list of known ChannelIDs and current versions. By default, this will return a list of ALL channels and known versions.
A client may provide a "If-Modified-Since" header (as specified in RFC2616), in which case the server will only return channel IDs that have been modified since the specified time. If no channels have been modified, the server will return a 304 Not Modified
error.
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
NOTE: the X-UserAgent-ID must be provided and match the value returned by the /register
call.
Arguments
There are no arguments for this call.
Returns
The server should return an object with the following fields:
- updates - An association of ChannelIDs and versions that have changed since the last time the UA called /update.
- expired - A list of ChannelIDs which have expired. The UA should remove these from its own database, and ask applications to re-register.
TODO We need acknowledgement of the /update from the UA. It is possible for the response to be lost in flight and we're trying for reliable delivery.
NOTE: If the server is unable to determine previous data for this X-UserAgent-ID, it will return a 410 leading to the Registration Sync Protocol occurring.
See POST /update
for details.
Example
GET http://push.m.o/v1/update --- {"updates": [{"channelID":"1ced595d7f6c9f60cc5c9395dc6b72aa7e1a69a7","version":"42"}, {"channelID":"bf08e25861c900c3ab343670eee1873d0b724eef","version":"1"}], "expired": ["channelID1", "channelID2", ...]}
POST /v1/update
Registration Sync Protocol
Refresh the server from the client. This request REQUIRES a X-UserAgent-ID header.
This call SHOULD only be performed by the UA after a 410 response from GET /v1/update. This call presumes the PushServer is in "recovery" mode after a serious failure where data is unavailable. (e.g. after a node crash or other unexpected activity).
If the UserAgent attempts to POST information to the server which already has data for that X-UserAgent-ID, or the PushServer is no longer in "recovery" mode, the server will deny the request with a 403 error. In that case, the UserAgent should request re-registration for all Channels.
The UserAgent SHOULD
- Send a list of <channelID, pushEndpoint, version> pairs capturing its current state to PushServer, along with the UAID.
- If the PushServer accepts the request, it replaces its records for the UAID with information from the client and begins accepting PUT /update/<ChannelID> requests from the AppServers.
- If the PushServer detects a duplicate UAID, it SHOULD respond with a 403 error.
- In this case, the client should invalidate its current state and ask apps application to re-register using the push-register system message. UserAgent and PushServer will now organically sync similar to standard /register calls.
Arguments
The POST body is a JSON encoded block:
{ "channels": [{"channelID": "channelID1", "pushEndpoint": "...", "version": "version1"}, {"channelID": "channelID2", "pushEndpoint": "...", "version": "version2"}, ...] }
Returns
{}
Errors
- 403 - The server may response with this for any reason, although usually it'll be due to duplicate UAID.
Example
POST http://push.m.o/v1/update {"channels":[{"channelID":"1ced595d7f6c9f60cc5c9395dc6b72aa7e1a69a7","version":"42"},{"channelID":"bf08e25861c900c3ab343670eee1873d0b724eef","version":"1"}]}
---
{}
PUT /v1/update/<ChannelID>
Update the version of a given channel.
Arguments should be passed as standard form data (enctype: multipart/form-data) This call does not require a X-UserAgent-ID, as it may be called by the AppServer.
Arguments
version=<version>
The version is the AppServer specific VersionID to use. This ID is opaque to the PushServer, however it is suggested that:
- The ID be sequential.
- The ID be less than 100 characters.
- The ID should be UTF8 compliant.
Returns
In the event of a PushServer catastrophe, the PushServer will be in "Recovery" mode. During this time, unresolvable ChannelIDs will return a 503 error.
In non-recovery mode, unresolvable pushEndpoints will return a 404. In this case, the app server should remove the endpoint association
A case can occur where the server starts in recovery mode, then after a sufficient period of time switches to non-recovery mode, but a particular UA has still not pinged it (perhaps because the UA does not have Internet access). In this case, application servers may have removed endpoint associations, so the push server needs to inform the UA about which channels were updated, but returned a 404. The UA will then request those apps to re-register.
The alternative to this 2 step recovery is to require all pushEndpoints to 404, and on Registration Sync, all apps are told to register again using 'push-register'.
This call returns an empty JSON object.
Example
PUT http://push.m.o/v1/update/foo1234 version=1.3 --- {}