1

Topic: enqueue_message and long polling.

AMansfield wrote:

dave wrote:

...
3. I cannot reorder/skip/remove videos in the queue on the TV. All possible with calls made via Flingo to the TV.
...

Maybe I'm missing something but this doesn't seem possible with the currently exposed "API".

My bad.  It is not possible with the APIs documented on flingo.org. Fling supports a generic enqueue message mechanism.  However, I am not yet satisfied that the semantics are what we want, so I delayed documenting them publicly.  At one time I had a more generic RPC mechanism which broke when I made some rather deep changes to the way fling works.

Consider this a request for comments more than true documentation, because it is highly likely to change.

     
    @fling_response
    @restrict_referer
    @annotate(guid=unicode, method=unicode, params=unicode, ttl=int)
    def enqueue_message(self, guid=u'', method=u'', params=None, ttl=600):

You may recognize this for what it is: Python.  All of the APIs on the server-side are written in Python.

Here fling_response, restrict_referer, and annotate are all python decorators.

restrict_referer prevents enqueue_message from being called from anything that has a referer URI containing non-whitelisted domains but allows requests with no referer URI header.  The absence of a referer URI is taken to mean that the message is being sent from a desktop application or bookmarklet.   This assumption is consistent with the behavior of all the modern browsers I tried: Firefox, Safari, IE, Opera, Chrome.  Currently only flingo.tv is whitelisted.  This ensures that arbitrary messages can only be passed via an iframe, web page, or SWF* (see comment #1) served from flingo.tv.  Not even flingo.org is whitelisted.  The restrict_referer decorator enforces the semantics of restricted calls (see http://flingo.org/developers.html#security_model for the definitions of public, private, and restricted calls).  By preventing web sites from sending arbitrary messages to network services, we prevent random web sites from doing things like removing queued items, queueing up spam, etc.  The iframe, web page, or SWF served from a trusted domain acts as a trusted intermediary that can enforce policy.

fling_response encodes anything returned as JSON.

annotate converts the string parameters in the URI query string to the appropriate type and raises an exception when the conversion fails.

guid if omitted causes the message to be private broadcast to all services in the user's private network.  If specified then the message is delivered to only that specified network service.

params is a URI-encoded JSON-encoded dict of key-value pairs where the value can be anything representable using JSON including arrays, dicts, and nested arrays and dicts.  Double encoding.  BLAH!

Comment #1: I put an asterisk next to SWF because browsers handle referer URIs inconsistently for Flash.  As a result, I have been playing with other mechanisms to secure flash such as requiring messages to sent using HTTP POSTs since most cross-site scripting hacks use HTTP GET  (SCRIPT, IFRAME, IMG).

WEAKNESSES:

  • only supports short messages since the params are encoded in the URI.

  • no ability to specify service name, so if two services run on a device and share GUID then there is no way to address one specific service.

  • uses rather weird special encoding for params.

  • does not work with any existing standard RPC libraries.

PROPOSED FIX:

  • deprecate passing params or method via URI.

  • use the URI as an envelope to specify destination GUID(s) and/or service names only.  If none provided then treat as private broadcast to all services in the user's private network.

  • require messages to be POSTed.

  • The POST body uses JSON-RPC.

By using JSON-RPC, a wide variety of existing libraries can be used to talk to devices.  To separate call from envelope, use the the URI of the RPC interface to scope the call to a specific device (guid), named service on that device (guid, service name pair) or named service on all devices (service name).  Something like:

   http://flingo.tv/fling/enqueue_message?guid=G&service=xyzmusicplayer

with body

  {"params": {"a": 10, "b": "boo"}, "jsonrpc": 2.0, "method": "foo", "id" : 0}

flingo.tv queues the message in a relay.  It may queue the message to multiple destinations, e.g., when guid is not provided in the call's envelope and there are multiple services in the network.  Currently service(s) obtain queued messages by long-polling.

    @fling_response
    @restrict_referer
    @annotate(guid=unicode, wait=int)
    def longpoll_messages(self, guid=u'', wait=60):

The server holds onto the request for a maximum of wait seconds.  It returns immediately with an array of messages if any were queued since the last call to longpoll_messages.

Still not completely clear how to handle return results when multiple services receive a single queued message.  Nothing would strictly adhere to the defined semantics of JSON-RPC.  One solution is to always return an array of dicts where each dict contains a result and the envelope (guid and/or service name) of the service returning the result.

I would like to extend longpoll to include a service name parameter so that each service running on a device can obtain messages addressed only for that service.

2

Re: enqueue_message and long polling.

i implemented enqueue and longpoll methods.  Enqueue returns a token that can be passed by longpoll to retrieve the return result, but I found these primitives overly burdensome.  To call a device requires minimally two calls and possibly many.  The semantics don't match JSON-RPC or XML-RPC.  It seems like overkill for the dominate use case: calling a device that is powered to perform an immediate action--- like a remote control.

The enqueue/longpoll primitives allow for an arbitrary time between when a call is made and when the result is returned, which handles the case when a device is powered down much of the time (a la Delay Tolerant Networking).  I think it is nice to have DTN primitives, but the non-DTN case is important enough to address with a simpler primitive.

To handle the remote control use case, I added a "call" method (see http://flingo.org/developers.html#calling) that works with JSON-RPC and XML-RPC and allows passing of arbitrary small opaque message bodies back and forth between a sandboxed application (web page) and a potentially sandboxed service in the same private network.

3

Re: enqueue_message and long polling.

I also implemented a call primitive, which is much easier to use than enqueue.  The problem with the enqueue call is that it returns immediately after the message is queued in the backend.  It doesn't wait for a response message from the receiving service.   To get the call response, the caller must call longpoll until the response arrives.

The call waits for a response from the receiving service.  As a result call can be used with existing JSON-RPC or XML-RPC libraries provided the receiving service understands JSON-RPC or XML-RPC.  Our flingo app in the Vizio and Western Digital boxes understands JSON-RPC.  The call primitive can be used as the basis for building web remote controls of TVs. 

Now I just need to flesh out and document the remote control API for our application.