Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Device APIs as discoverable HTTP service endpoints

161 views
Skip to first unread message

Rich Tibbett

unread,
Aug 23, 2011, 12:40:12 PM8/23/11
to dev-w...@lists.mozilla.org
I wonder if we can reduce the problem of device apis to one of simply
allowing other native apps (or the web browser...or web sites for that
matter) to create HTTP service endpoints and then just provide the
necessary generic mechanisms for web pages to discover and access those
advertised endpoints based on simple, decentralized service type
identifiers.

The UA would discover local services via a well-defined service
discovery mechanism, then broker the user authorization required for a
web page requesting a device api to obtain access to a service's HTTP
endpoint that matched the service type originally requested by that page.

In this model it is not necessary to define the APIs up front (although
anyone is free to do that if they wish). Service type identifiers need
no central management authority. Instead, we allow _any_ native app / OS
/ website to offer its own API. We allow free-market forces to naturally
select the best API(s) offered for any given problem as they spring up.
What comes to be most used and the definition of the term 'best' is up
to the market to decide: millions of web apps deciding which API(s) to
plug in to. The selection of the 'best' API(s) can, of course, evolve
over time. This model works in the interests of a constantly evolving web.

Here is a first draft spec for this proposal:

http://people.opera.com/richt/release/specs/discovery/Overview.html

Here is a sample web page's javascript for interacting with e.g. a
native calculator service provided from the API proposed above:

navigator.getNetworkServices('_calculator._http._tcp', function(services) {
var xhr = new XmlHttpRequest();
xhr.open('POST', services[0].url); // cross-domain enabled
// service url endpoint (see spec)
xhr.onreadystatechange = function(response) {
if(xhr.readyState==4)
console.log('Result received');
// ...
};
xhr.send('{value:"7251*8"}');
}, false);

My 'calculator api' above is deliberately lame ;). Someone somewhere
will eventually write a super-scientific calculator api, offered on a
different service type identifier, and that would usurp my API attempt
pretty quickly as the most widely adopted service endpoint by web apps
in the know. 'Best' in this case would be because that new API simply
offered more powerful functionality. It may also simply be deployed in
more devices. "Best" is really subjective based on a huge amount of factors.

My question for this list is whether such an idea has been considered as
a kick-off point for this project or whether there would be any interest
in pursuing such a model to open the device window to the web. Please
feel free to discuss any specifics of this proposal on this list.

Thanks and all the best,

- Rich T

--
Platform Architect, CORE
Opera Software ASA

Doug Turner

unread,
Aug 23, 2011, 1:19:40 PM8/23/11
to Kevin Gadd, Rich Tibbett, dev-w...@lists.mozilla.org
Using HTTP for device discovery sounds nice, but it is pretty cumbersome from a developers point of view.

HTTP Model:


>> navigator.getNetworkServices('_calculator._http._tcp', function(services) {
>> var xhr = new XmlHttpRequest();
>> xhr.open('POST', services[0].url); // cross-domain enabled
>> // service url endpoint (see spec)
>> xhr.onreadystatechange = function(response) {
>> if(xhr.readyState==4)
>> console.log('Result received');
>> // ...
>> };
>> xhr.send('{value:"7251*8"}');
>> }, false);


The Standard Model:

if (navigator.calculator)
navigator.calculator.calc("7251*8", onResult);


Tell me which you'd rather develop application with?

Regards,
Doug

On Aug 23, 2011, at 10:03 AM, Kevin Gadd wrote:

> I had a similar discussion with Mike Shaver about this, and we thought
> it might be interesting to pursue exposing device APIs as shared
> workers. This provides nice encapsulation and reliability benefits
> without requiring actual network traffic, and allows for exchanging
> JSON data bundles in a bidirectional manner. I don't think any work
> has been done on prototyping that, though, since we don't have a
> finished shared worker implementation yet.
>
> I like HTTP endpoints, but the requirement to use a network stack and
> create HTTP payloads for every message you send to the device seems
> painful.
>
> -kg

>> _______________________________________________
>> dev-webapi mailing list
>> dev-w...@lists.mozilla.org
>> https://lists.mozilla.org/listinfo/dev-webapi
>>
> _______________________________________________
> dev-webapi mailing list
> dev-w...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-webapi

Rich Tibbett

unread,
Aug 23, 2011, 1:20:32 PM8/23/11
to Kevin Gadd, dev-w...@lists.mozilla.org
Kevin Gadd wrote:
> I had a similar discussion with Mike Shaver about this, and we thought
> it might be interesting to pursue exposing device APIs as shared
> workers. This provides nice encapsulation and reliability benefits
> without requiring actual network traffic, and allows for exchanging
> JSON data bundles in a bidirectional manner. I don't think any work
> has been done on prototyping that, though, since we don't have a
> finished shared worker implementation yet.

I'd be interested in hearing more about that if you were planning to
share some high-level details at some point.

>
> I like HTTP endpoints, but the requirement to use a network stack and
> create HTTP payloads for every message you send to the device seems
> painful.

It's really the same as using existing APIs like XHR, WebSockets or
Server Sent Events though. So you get channel and payload flexibility
thrown in for free without actually increasing today's programming
complexity.

That flexibility also means we can build services to interoperate with
existing services that may be advertised on a local device, such as the
iTunes library for example.

Doug Turner

unread,
Aug 23, 2011, 1:37:06 PM8/23/11
to Rich Tibbett, dev-w...@lists.mozilla.org, Kevin Gadd

On Aug 23, 2011, at 10:28 AM, Rich Tibbett wrote:

> Doug Turner wrote:
>> Using HTTP for device discovery sounds nice, but it is pretty cumbersome from a developers point of view.
>>
>> HTTP Model:

>>>> navigator.getNetworkServices('_calculator._http._tcp', function(services) {
>>>> var xhr = new XmlHttpRequest();
>>>> xhr.open('POST', services[0].url); // cross-domain enabled
>>>> // service url endpoint (see spec)
>>>> xhr.onreadystatechange = function(response) {
>>>> if(xhr.readyState==4)
>>>> console.log('Result received');
>>>> // ...
>>>> };
>>>> xhr.send('{value:"7251*8"}');
>>>> }, false);
>>
>>

>> The Standard Model:
>>
>> if (navigator.calculator)
>> navigator.calculator.calc("7251*8", onResult);
>>
>>
>> Tell me which you'd rather develop application with?
>

> This is a trick question :) I'd go with the standard model please.

I don't care at all about standards here. I care about making something that I would enjoy writing applications with. lets code please.

Doug

Rich Tibbett

unread,
Aug 23, 2011, 1:37:55 PM8/23/11
to Doug Turner, dev-w...@lists.mozilla.org, Kevin Gadd

Rich Tibbett wrote:
> Doug Turner wrote:
>> Using HTTP for device discovery sounds nice, but it is pretty
>> cumbersome from a developers point of view.
>>
>> HTTP Model:
>>>> navigator.getNetworkServices('_calculator._http._tcp',
>>>> function(services) {
>>>> var xhr = new XmlHttpRequest();
>>>> xhr.open('POST', services[0].url); // cross-domain enabled
>>>> // service url endpoint (see spec)
>>>> xhr.onreadystatechange = function(response) {
>>>> if(xhr.readyState==4)
>>>> console.log('Result received');
>>>> // ...
>>>> };
>>>> xhr.send('{value:"7251*8"}');
>>>> }, false);
>>
>>
>> The Standard Model:
>>
>> if (navigator.calculator)
>> navigator.calculator.calc("7251*8", onResult);
>>
>>
>> Tell me which you'd rather develop application with?
>
> This is a trick question :) I'd go with the standard model please.

Sorry, not a trick question, just a good question that brings out an
important aspect of the proposal: that service-level logic is left to
web pages that are also free to abstract service-level messaging in to
simple reusable JS APIs if they so wish.

>
> If I wanted I could wrap my service communication in to such an API in
> pure JS. I may even provide that as an open-source library somewhere on
> the web for others to embed and use in their own web pages.
>
> So a standard model that doesn't necessarily have to be hard-coded in
> the UA with the plug in to the best calculator api on the device or
> elsewhere.
>
>>
>> Regards,
>> Doug


>>
>> On Aug 23, 2011, at 10:03 AM, Kevin Gadd wrote:
>>
>>> I had a similar discussion with Mike Shaver about this, and we thought
>>> it might be interesting to pursue exposing device APIs as shared
>>> workers. This provides nice encapsulation and reliability benefits
>>> without requiring actual network traffic, and allows for exchanging
>>> JSON data bundles in a bidirectional manner. I don't think any work
>>> has been done on prototyping that, though, since we don't have a
>>> finished shared worker implementation yet.
>>>

>>> I like HTTP endpoints, but the requirement to use a network stack and
>>> create HTTP payloads for every message you send to the device seems
>>> painful.
>>>

>>>> navigator.getNetworkServices('_calculator._http._tcp',
>>>> function(services) {
>>>> var xhr = new XmlHttpRequest();
>>>> xhr.open('POST', services[0].url); // cross-domain enabled
>>>> // service url endpoint (see spec)
>>>> xhr.onreadystatechange = function(response) {
>>>> if(xhr.readyState==4)
>>>> console.log('Result received');
>>>> // ...
>>>> };
>>>> xhr.send('{value:"7251*8"}');
>>>> }, false);
>>>>

Natanael L

unread,
Aug 24, 2011, 6:31:17 AM8/24/11
to mozilla-d...@lists.mozilla.org

This looks a bit like the discussion here: https://bugzilla.mozilla.org/show_bug.cgi?id=674720

Rich Tibbett

unread,
Aug 24, 2011, 7:22:07 PM8/24/11
to Natanael L, mozilla-d...@lists.mozilla.org
Natanael L wrote:
> On 23 Aug, 18:40, Rich Tibbett<ri...@opera.com> wrote:
>> I wonder if we can reduce the problem of device apis to one of simply
>> allowing other native apps (or the web browser...or web sites for that
>> matter) to create HTTP service endpoints and then just provide the
>> necessary generic mechanisms for web pages to discover and access those
>> advertised endpoints based on simple, decentralized service type
>> identifiers.
>>

[snip]

>>
>> Here is a first draft spec for this proposal:
>>
>> http://people.opera.com/richt/release/specs/discovery/Overview.html
>>

[snip]

>
> This looks a bit like the discussion here: https://bugzilla.mozilla.org/show_bug.cgi?id=674720

FWIW, I briefly suggested an extension to this proposal for a Web
Intents system based on the well-established discovery protocol of DNS-SD:

http://lists.w3.org/Archives/Public/public-device-apis/2011Aug/0018.html

I'd like to explore that further as a better model for Web Intents.

Robin Berjon

unread,
Aug 25, 2011, 7:12:41 AM8/25/11
to Kevin Gadd, Rich Tibbett, dev-w...@lists.mozilla.org
On Aug 23, 2011, at 19:03 , Kevin Gadd wrote:
> I had a similar discussion with Mike Shaver about this, and we thought
> it might be interesting to pursue exposing device APIs as shared
> workers. This provides nice encapsulation and reliability benefits
> without requiring actual network traffic, and allows for exchanging
> JSON data bundles in a bidirectional manner. I don't think any work
> has been done on prototyping that, though, since we don't have a
> finished shared worker implementation yet.

That's certainly an interesting idea; do you happen to have some napkin sketch of what you had in mind?

> I like HTTP endpoints, but the requirement to use a network stack and
> create HTTP payloads for every message you send to the device seems
> painful.

It ought to be possible to get away without necessarily hitting the network. You could have something like:

var contacts = createServiceURI("contacts");
var xhr = new XMLHttpRequest();
xhr.open("GET", contacts);
// etc.

Note that I used createServiceURI() to mimic the blob system, but the URI could come from wherever else makes sense. If the service happens to be an HTTP endpoint, then you get an HTTP URI, but if it's locally implemented you might get a URI looking like service:DEADBEEF that would not hit the network but return information directly. Last time this discussion came up with the very same concerns over network performance I hacked together a demo that shows the concept: http://dev.w3.org/2009/dap/docs/lrest/gallery-lrest.html. No need to say that this could be implemented more cleanly by the UA. There are more details on that hack in the discussion at http://lists.w3.org/Archives/Public/public-device-apis/2010Feb/0085.html.

--
Robin Berjon - http://berjon.com/ - @robinberjon

Kevin Gadd

unread,
Aug 25, 2011, 7:31:08 AM8/25/11
to Robin Berjon, Rich Tibbett, dev-w...@lists.mozilla.org
I don't think my discussion with Mike ever got to the point of
thinking about APIs, but what I envision is roughly like this:

You implement a service (or, more precisely, a WebUSB driver) in the
form of a shared worker. The worker accepts messages from any webpage
(or other worker) that decides to speak to it, using whatever protocol
you decide is appropriate. So, hypothetically, if I wrote a driver for
the Kinect camera using WebUSB, I'd have a simple USB shared worker
that was responsible for handling incoming RGB/Z frames from the
camera and doing things like adjusting zoom and tilt. To consume the
camera, a webpage (or other worker) would open a port to the shared
worker and send it a message to request access to the camera. The
kinect camera driver worker would then respond by making sure the
camera was actually active, and then return a response message giving
the page an identifier it could use to further interact with the
camera or to release control of the camera. At that point, the webpage
could request rgb/z frames from the camera by sending a message, or
the camera driver could periodically send new frames to the owning
page by posting a message in the opposite direction.

Like a HTTP based mechanism, this doesn't have to be specified
particularly narrowly; the real value is in having services like this
written (as much as possible) in JS running in a shared worker. I
think this offers multiple real benefits:
Stability - If the worker fails, you can restart it. The messaging
protocol means it's also trivial to run the worker in an entirely
separate process.
Security - All you can send are JSON payload messages, so I think that
limits the attack surface significantly versus a custom-tailored
native IDL API + C++ implementation.
Composability - A messaging oriented protocol will lead to APIs that
are naturally easier to reuse and build upon. In particular, this will
make it straightforward to build higher-level services atop
lower-level ones.
Performance - A shared worker approach has better potential for good
performance, since it's at least theoretically possible to exchange
JSON data structures between workers without stringifying them.
Likewise, you aren't necessarily burdened with the complexity of HTTP,
TCP, port assignment, etc. In cases where a javascript implementation
is not performant enough it is also much simpler to expose a native
C++ implementation through a shared worker protocol than exposing it
through a full HTTP server (in my opinion, at least).

-kg

Robin Berjon

unread,
Aug 25, 2011, 7:49:44 AM8/25/11
to Doug Turner, Rich Tibbett, Kevin Gadd, dev-w...@lists.mozilla.org
On Aug 23, 2011, at 19:19 , Doug Turner wrote:
> Using HTTP for device discovery sounds nice, but it is pretty cumbersome from a developers point of view.
>
> HTTP Model:
>>> navigator.getNetworkServices('_calculator._http._tcp', function(services) {
>>> var xhr = new XmlHttpRequest();
>>> xhr.open('POST', services[0].url); // cross-domain enabled
>>> // service url endpoint (see spec)
>>> xhr.onreadystatechange = function(response) {
>>> if(xhr.readyState==4)
>>> console.log('Result received');
>>> // ...
>>> };
>>> xhr.send('{value:"7251*8"}');
>>> }, false);
>
> The Standard Model:
>
> if (navigator.calculator)
> navigator.calculator.calc("7251*8", onResult);
>
> Tell me which you'd rather develop application with?

Well, duh, both of course!

I think that there are two topics that we should keep separated here. The first is that of discovery. Discovery is dead useful, and not just as a way of accessing the sort of services that Richard describes but more generally of accessing services on the local network. It *could* indeed be used to discover services that the user agent knows about more generally than by querying the local network but that is a small piece of glue that I don't think we should mix up with the rest just yet. You could have a contacts server on the local network that the Discovery API would list based on a DNS-SD investigation, and you could equally have a remote contacts server somewhere on the Web that the same Discovery API would list based on it being "installed" into the UA through a mechanism like Intents/Activities.

But that is separate from the question of whether a certain subset of device APIs are better exposed as an HTTP service or as a more traditional API. I think that the answer boils down to asking:

• Is it useful to expose multiple arbitrary sources for such a service? and
• If so, how are new sources acquired?

At least for APIs such as Contacts and Calendar, I think that the answer to the first question is yes. I want to grab contacts from my system's database, from all the various social networks I'm one, I want to be able to expose such a service for community sites I run, etc. Looking at the second question, if we take Labs' prototype of the Contacts API it did indeed support multiple sources but one could not add arbitrary sources without hacking the code directly. It would certainly be possible to design some form of extension system but in order to be appealing it would need to be relatively easy to put together and standardised across UAs. Intents/Activities may help there, but my hope here would be that all that they would do in such a situation is a provide scripts interested in contacts with an opaque URI to the service endpoint while handling user acquiescence.

Using an HTTP binding, getting multiple arbitrary endpoints for a given service is a no-brainer.

Now this leaves the style question you raise. I meant it when I said that you can have both. Assuming the following WebIDL:

interface Calculator {
void calc(DOMString expr, CalcResultCB success, CalcErrorCB error);
};

[Callback=FunctionOnly, NoInterfaceObject]
interface CalcResultCB {
void onsuccess (float result);
};

[Callback=FunctionOnly, NoInterfaceObject]
interface CalcErrorCB {
void onerror (DOMString error);
};

You can generate a client library that exposes a nice API of the kind you describe as well as a JSON-RPC protocol description for the interchange (for the same price, I'll throw in stubs for a server implementation).

So on the client you'd get something like:

var coolCalc = new Calculator("http://berjon.com/endpoints/calc");
coolCalc.calc("17*42", function (res) { alert("Result: " + res); }, function (err) { alert("BOOM!");});

And on the wire you'd get:

-->
POST /endpoints/calc HTTP/1.1
Host: berjon.com
Content-Type: application/json-rpc
Content-Length: xxx

{
"jsonrpc": "2.0",
"method": "Calculator.calc",
"params": { "expr": "17*42"},
"id": "DEADBEEF"
}

<--
HTTP/1.1 200 OK
Content-Type: application/json-rpc

{
"jsonrpc": "2.0",
"result": 714,
"id": "DEADBEEF"
}

This gives you a defined interoperable protocol that can support arbitrary sources and a nice API in one bundle (sweeping some issues with WebIDL's generality under the rug for a while). Now if you could also stop having interesting conversations for a short while, I might finish my prototype of this ;) (It seems that Andreas has made most of the hard parts for this as part of dom.js anyway).

Brian LeRoux

unread,
Aug 25, 2011, 9:41:56 AM8/25/11
to Robin Berjon, Rich Tibbett, Kevin Gadd, Doug Turner, dev-w...@lists.mozilla.org
Sorry for the the late chime in but agree w/ Robin here that this type
of arch only makes sense for certain types of data centric APIs.
Anything realtime-ish or stream-ish doesn't make much sense. (Like an
accelerometer.)

Data APIs, however, it can be nice. (Contacts, Calendar, Media)

While we're at it can we redesign the mess that is the XmlHTTPRequest?

I am loving the style tj has come up with for his 'superagent' lib.

http://visionmedia.github.com/superagent/

0 new messages