Networked Service Discovery and Messaging

Unofficial Draft 11 May 2012

Previous version:
Editors:
Rich Tibbett, Opera Software ASA
Clarke Stevens, CableLabs

Abstract

This specification defines a mechanism for an HTML document to discover and subsequently communicate with HTTP-based services advertised via common discovery protocols within a user's network.

Status of This Document

This document is merely a public working draft of a potential specification. It has no official standing of any kind and does not represent the support or consensus of any standards organisation.

Table of Contents

1. Introduction

This section is non-normative.

To enable Web pages to connect and communicate with Local-networked Services provided over HTTP, this specification introduces the NavigatorNetworkService interface.

Using this API consists of requesting a well-known service type, known by developers and advertised by Local-networked Devices. User authorization, where the user connects the web page to one or more discovered services, is expected before the web page is able to interact with any Local-networked Services.

A web page creates a request to obtain connectivity to services running in the network by specifying a well-known discovery service type that it wishes to interact with.

The user agent, having captured all advertised services on the network from the Service Discovery mechanisms included in this recommendation, attempts to match the requested service type to a discovered service according to the processing described herein.

If a service connectivity request is successful then the Web page is provided with the necessary information to communicate with the authorized Local-networked Service. If the request fails then the Web page will receive an error callback containing an error code describing the cause of Local-networked Service connectivity failure.

Once connected to a Local-networked Service the Web page can send requests and receive responses to the Local-networked Service via the messaging format and appropriate channel inferred from the service type authorized via the provided API. The Web page, once connected, can also receive service-pushed events, in the messaging format supported by the Local-networked Device, if such event subscription functionality is provided by the connected Local-networked Service.

Example of requesting a DNS-SD advertised service:


function showServices( servicesList ) {
  // Show a list of all the services provided to the web page
  for(var i in servicesList.services) console.log( servicesList.services[i].name );
}
 
navigator.getNetworkServices('_boxee-jsonrpc._tcp', showServices);

Example of requesting a UPnP advertised service, also handling error conditions:


function showServices( servicesList ) {
  // Show a list of all the services provided to the web page
  for(var i in servicesList.services) console.log( servicesList.services[i].name );
}
 
function error( e ) {
  console.log( "Error occurred: " + e.code );
}
 
navigator.getNetworkServices('urn:schemas-upnp-org:service:ContentDirectory:1', showServices, error);

Example of requesting either a DNS-SD or UPnP advertised service:


function showServices( servicesList ) {
  // Show a list of all the services provided to the web page (+ service type)
  for(var i in servicesList.services)
     console.log( servicesList.services[i].name + '(' + servicesList.services[i].type + ')' );
}
 
navigator.getNetworkServices([
  '_boxee-jsonrpc._tcp',
  'urn:schemas-upnp-org:service:ContentDirectory:1'
], showServices);

For more detailed examples see the Examples section.

2. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words must, must not, required, should, should not, recommended, may, and optional in this specification are to be interpreted as described in [RFC2119].

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Some conformance requirements are phrased as requirements on attributes, methods or objects. Such requirements are to be interpreted as requirements on user agents.

Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)

The only conformance class defined by this specification is user agents.

User agents may impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations.

When support for a feature is disabled (e.g. as an emergency measure to mitigate a security problem, or to aid in development, or for performance reasons), user agents must act as if they had no support for the feature whatsoever, and as if the feature was not mentioned in this specification. For example, if a particular feature is accessed via an attribute in a Web IDL interface, the attribute itself would be omitted from the objects that implement that interface - leaving the attribute on the object but making it return null or throw an exception is insufficient.

2.1 Dependencies

This specification relies on several other underlying specifications.
HTML
Many fundamental concepts from HTML are used by this specification. [HTML5]
WebIDL
The IDL blocks in this specification use the semantics of the WebIDL specification. [WEBIDL]

3. Terminology

The construction "a Foo object", where Foo is actually an interface, is sometimes used instead of the more accurate "an object implementing the interface Foo".

The term DOM is used to refer to the API set made available to scripts in Web applications, and does not necessarily imply the existence of an actual Document object or of any other Node objects as defined in the DOM Core specifications. [DOM-CORE]

An IDL attribute is said to be getting when its value is being retrieved (e.g. by author script), and is said to be setting when a new value is assigned to it.

A valid service type is a string that only uses characters in the ranges U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E, U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.

A valid service type provided in the type attribute of the getNetworkServices() method will be matched against the services currently contained in the list of available service records according to the algorithms defined in this specification.

4. Requesting networked services

module window {
    
    interface [
        GenerateIsReachable=ImplFrame,
        OmitConstructor
    ] Navigator {
        readonly attribute DOMString appCodeName;
        readonly attribute DOMString appName;
        readonly attribute DOMString appVersion;
        readonly attribute DOMString language;
        readonly attribute DOMString userAgent;
        readonly attribute DOMString platform;
        readonly attribute DOMPluginArray plugins;
        readonly attribute DOMMimeTypeArray mimeTypes;
        readonly attribute DOMString product;
        readonly attribute DOMString productSub;
        readonly attribute DOMString vendor;
        readonly attribute DOMString vendorSub;
        readonly attribute boolean cookieEnabled;
        boolean javaEnabled();
    
        readonly attribute boolean onLine;
    
        void getNetworkServices(
            in DOMString type,
            in [Callback=FunctionOnly]  NavigatorNetworkServiceSuccessCallback successCallback,
            in [Callback=FunctionOnly, Optional] NavigatorNetworkServiceErrorCallback errorCallback );
    
        void onNetworkServices(
            in DOMString type,
            in [Callback=FunctionOnly]  NavigatorNetworkDiscoveryCallback serviceAddedCallback,
            in [Callback=FunctionOnly]  NavigatorNetworkDiscoveryCallback serviceRemovedCallback,
            in [Callback=FunctionOnly, Optional] NavigatorNetworkServiceErrorCallback errorCallback );
    
        void onNetworkEvent(
            in DOMString type,
            in [Callback=FunctionOnly]  NavigatorNetworkEventCallback msgCallback,
            in [Callback=FunctionOnly, Optional] NavigatorNetworkServiceErrorCallback errorCallback );
    
    #if defined(ENABLE_GEOLOCATION) && ENABLE_GEOLOCATION
        readonly attribute [EnabledAtRuntime] Geolocation geolocation;
    #endif
    
    #if defined(ENABLE_DOM_STORAGE) && ENABLE_DOM_STORAGE
        void getStorageUpdates();
    #endif
    
    #if defined(ENABLE_REGISTER_PROTOCOL_HANDLER) && ENABLE_REGISTER_PROTOCOL_HANDLER
        void registerProtocolHandler(in DOMString scheme, in DOMString url, in DOMString title)
            raises(DomException);
    #endif
    
    #if defined(ENABLE_MEDIA_STREAM) && ENABLE_MEDIA_STREAM
        [Custom, EnabledAtRuntime] void webkitGetUserMedia(in DOMString options,
            in [Callback=FunctionOnly] NavigatorUserMediaSuccessCallback successCallback,
            in [Callback=FunctionOnly, Optional] NavigatorUserMediaErrorCallback errorCallback)
        raises(DOMException);
    #endif
    };
}

4.1 Methods

window . navigator . getNetworkServices ( type , successCallback [, errorCallback ] )

Prompts the user to select one or more discovered network services that have advertised support for the requested service type.

The type argument contains one or more valid service type tokens that the web page would like to interact with.

If the user accepts, the successCallback is invoked, with one or more NetworkService objects as its argument.

If the user declines, the errorCallback (if any) is invoked.

When the getNetworkServices(type, successCallback[, errorCallback]) method is called, the user agent must run the following steps:

  1. If type is a string consisting of one valid service type token, then let requested control types be an array containing one item with a value of type and continue to the step labeled process below.
  2. Continue to the step labeled failure below.
  3. Process: Let services found be an empty array.
  4. For each available service in the list of available service records run the following steps:
    1. For each requested service type in requested service types: If available service's type attribute equals the requested service type then let matched service equal the value of available service and continue at the step labeled attach below.
    2. Continue at the next available service.
    3. Attach: If matched service is not empty then run the following steps:
      1. Let new service object be a new NetworkService object, mapping the parameters of matched service to this new object where possible.
      2. Append new service object to the services found array.
  5. If services found is an empty array, then jump to the step labeled failure below.
  6. Return, and run the remaining steps asynchronously.
  7. Optionally, e.g. based on a previously-established user preference, for security reasons, or due to platform limitations, jump to the step labeled failure below.
  8. The user agent must prompt the user in a user-agent-specific manner for permission to provide the entry script's origin with an array of NetworkService objects representing the user-authorized subset of services found.

    If the user grants permission to access one or more networked services then user agents should include an "ongoing local-network communication" indicator.

    If the user denies permission, jump to the step labeled failure below. If the user never responds, this algorithm stalls on this step.
  9. Let services be the array of one or more NetworkService objects for which the user granted permission.
  10. For each Object service in services, run the following substeps:
    1. Add the service's url parameter to the entry script origin's URL whitelist.
    2. If service was originally created from a UPnP discovery process and the service's eventsUrl parameter is not empty then setup a UPnP Events Subscription for service.
  11. Create a new NetworkServices object.
  12. Set NetworkServices servicesAvailable attribute to the length of services.
  13. The user agent must queue a task to invoke successCallback with services manager as its argument.
  14. The user agent must abort any remaining steps.
  15. Failure: If errorCallback is null or is not an object of type Function, then the user agent must abort these steps.
  16. Let error be a new NavigatorNetworkServiceError object whose code attribute has the numeric value 1 (PERMISSION_DENIED_ERR).
  17. The user agent must queue a task to invoke errorCallback with error as its argument.

The task source for these tasks is the user interaction task source.

When a NetworkService object is provided to a Web page, the user agent must add the url property to the entry script origin's URL whitelist. This list enables the Web page to override and initiate cross-site resource requests towards these URLs, and any sub-resources of these URLs, within the current entry script's origin via various existing mechanisms (e.g. Web Sockets, Server-Sent Events, Web Messaging, XMLHttpRequest).

If the user navigates away from the current browsing context, the user agent must remove all previously whitelisted urls from the entry script origin's URL whitelist. There is no persistence to network service selections provided to a web page. It is not possible to access a previously white-listed networked service without the necessary user authorization in all of the following cases:

  • If the current script is reloaded at any point in the same or different window.
  • if the current script reinvokes the getNetworkServices() method at any point in its execution.
  • If the user navigates forward or back in their history to reload the current page.
  • If a script is running in a different origin.

4.2 Error Handling

error . code

Returns the current error's error code. At this time, this will always be 1 or 2, for which the constants PERMISSION_DENIED_ERR and BAD_TYPE_PARAMETER are defined.

The code attribute of a NavigatorNetworkServiceError object must return the code for the error, which will be the following:

PERMISSION_DENIED_ERR (numeric value 1)
The user denied the page permission to access any networked devices.
BAD_TYPE_PARAMETER_ERR (numeric value 2)
The type parameter must begin with upnp: or zeroconf:.

5. Obtaining networked services

The NavigatorNetworkServices interface is the top-level response object from a call to getNetworkServices() and provides a set of user-authorized NavigatorNetworkService objects for the given request.

module window {
    interface [
        CustomMarkFunction
        ] NavigatorNetworkServices {
    
        // NavigatorNetworkService item(index)
        [Custom] void item(in unsigned long index);
    
        readonly attribute unsigned long length;
    
        readonly attribute unsigned short servicesAvailable;
    };
}

5.1 Attributes

services

A list of one or more NetworkService objects representing user-selected and user-authorized service endpoints.

servicesAvailable

Returns the current number of services matching one of the app-requested valid service types that are actively available within the user's current network.

The services attribute must return the initial list of NetworkService objects that have been selected as part of the algorithm for requesting networked services. This object is immutable meaning that it cannot change without the application re-invoking the getNetworkServices() method.

Services available within the local network can connect and disconnect at different times during the execution of a web page. A user agent can inform a web page when the state of networked services matching the requested valid service type change. Web pages can use this information to enable in-page experiences for communicating the state of networked services with the ability to change the particular service or set of services the page is connected to by re-invoking the getNetworkServices() method.

The servicesAvailable attribute must return the number of services available in the user's network that match the valid service type that was initially used to create the current NetworkServices object. By default, servicesAvailable must be set to 1.

When a previously unknown instance of a networked service matching one or the requested valid service types becomes available on the user's current network, the user agent must fire a new simple event at the onserviceavailable event handler.

When a previously known instance of a networked service matching one or the requested valid service types becomes unavailable on the user's current network, the user agent must fire a new simple event at the onserviceunavailable event handler.

5.2 Events

The following are the event handlers (and their corresponding event handler event types) that must be supported, as IDL attributes, by all objects implementing the NetworkServices interface:

Events are set up through the Navigator object.

Navigator.onNetworkServices() sets up callbacks that are called when devices are added or removed.

Navigator.onNetworkEvent() sets up a callback that is called when services generate events. The argument is:

module window {
    interface [
        CustomMarkFunction
        ] NavigatorNetworkEvent {
    
        readonly attribute DOMString propertyset;   // Properties sent from event device in XML
    
        readonly attribute DOMString uuid;          // device id
    
        readonly attribute DOMString friendlyName;
    
        readonly attribute DOMString serviceType;
    };
}

6. Communicating with a networked service

The NavigatorNetworkService interface is used to provide a set of connection information for an HTTP service endpoint and if available, service events, running on a networked device.

module window {
    interface [
        CustomMarkFunction
        ] NavigatorNetworkService {

        readonly attribute DOMString name;  // name of device
        readonly attribute DOMString type;  // service type id
        readonly attribute DOMString url;   // get info
        readonly attribute DOMString uuid;  // id of device
    };
}

6.1 Attributes

service . name

The name of the user-selected service.

service . type

The valid service type token value of the user-selected service.

service . url

The control URL endpoint (including any required port information) of the user-selected control service that has been added to the entry script origin's URL whitelist.

service . uuid

The unique id of the device.

The name attribute represents a human-readable title for the service.

The type attribute reflects the value of the valid service type of the service.

The url attribute is an absolute URL pointing to the root HTTP endpoint for the service that has been added to the entry script origin's URL whitelist. Web pages can subsequently use this value for implicit cross-document messaging via various existing mechanisms (e.g. Web Sockets, Server-Sent Events, Web Messaging, XMLHttpRequest).

The uuid attribute provides the unique id of the device.

7. Communicating with a Zeroconf (mDNS + DNS-SD)networked service

The Zeroconf NavigatorNetworkService object has the same members as the UPnP NavigatorNetworkService object and is as follows:

module window {
    interface [
        CustomMarkFunction
        ] NavigatorNetworkService {
                  
        readonly attribute DOMString name;  // name of device
        readonly attribute DOMString type;  // service type id
        readonly attribute DOMString url;   // get info
        readonly attribute DOMString uuid;  // id of device
    };
}
              

7.1 Attributes

service . name

The name of the user-selected service.

service . type

The valid service type token value of the user-selected service.

service . url

The control URL endpoint (including any required port information) of the user-selected control service that has been added to the entry script origin's URL whitelist.

service . uuid

The unique id of the device.

The name attribute represents a human-readable title for the service.

The type attribute reflects the value of the valid service type of the service.

The url attribute is an absolute URL pointing to the root HTTP endpoint for the service that has been added to the entry script origin's URL whitelist. Web pages can subsequently use this value for implicit cross-document messaging via various existing mechanisms (e.g. Web Sockets, Server-Sent Events, Web Messaging, XMLHttpRequest).

The uuid attribute provides the unique id of the device.

8. Service Discovery

User agents conforming to this specification must implement both SSDP [UPNP] and Zeroconf [ZEROCONF] service discovery mechanisms to enable Web pages to request and connect with HTTP services running on networked devices, discovered via either mechanism, through this API.

This section presents how the results of these two service discovery mechanisms will be matched to requested service types and how their properties will be applied to any resulting NetworkService objects.

It is expected that user agents will perform these service discovery mechansisms asynchronously and periodically update the list of networked devices as required. The timing of any service discovery mechanisms is an implementation detail left to the discretion of the implementer (e.g. once on user agent start-up, every X seconds during user agent execution or on invocation of this API from a Web page).

The list of available service records is a single dynamic internal lookup table within user agents that is used to track the current services available in the network at any given time. At any point during the running of either of the two service discovery mechanisms then existing entries within this table can be updated, entries can be added and entries can be removed as the status of networked services changes. Each record contained within this table contains the attributes: id, name, type, url and config.

8.1 Zeroconf (mDNS + DNS-SD)

For each DNS response received from a user-agent-initiated Multicast DNS Browse for PTR records with the name _services._dns-sd._udp on the resolved recommended automatic browsing domain [MDNS], the user agent must run the following steps:

  1. Let service mDNS responses be an array of PTR records received by issuing a Multicast DNS Browse for PTR records with the name of the current discovered service type.
  2. For each Object service mDNS response in service mDNS responses, run the following steps:
    1. Let network service record be an Object consisting of the following empty properties: id, name, type, url, config.
    2. Set network service record's id property to the value of the full PTR Service Instance Name [MDNS].
    3. Set network service record's name property to the value of the PTR Service Instance Name's Instance component [MDNS].
    4. Set network service record's type property to the value of the PTR Service Instance Name's Service component [MDNS].
    5. Set network service record's url property to the resolvable Service URL obtained from performing an DNS-SD Lookup [DNS-SD] of the current service from the PTR record provided [MDNS].
    6. Set network service record's config property to the string value of the contents of the first DNS-SD TXT record associated with the service mDNS response as defined in [DNS-SD].
    7. For each Object existing service record in the current list of available service records, run the following sub-steps:
      1. If the existing service record's id property matches the value of the network service record's id, then set the value of existing service record in the current list of available service records to the value of the network service record and skip the next step.
    8. Add network service record to the list of available service records.
    9. For each non-garbage collected NetworkService object run the following steps:
      1. If the NetworkService object was created from a type attribute but that attribute does not equal the current network service record's type property then continue at the next available active NetworkService object.
      2. Increment the servicesAvailable attribute of the NetworkServices object by 1.

8.2 Universal Plug-and-Play (UPnP)

For each SSDP Presence Announcement [UPNP] - a HTTP NOTIFY request - received from a user-agent-initiated SSDP Discovery Request [UPNP], the user agent must run the following steps:

  1. Let ssdp device be an Object with a property for each HTTP header received in the received SSDP Presence Announcement, with each key being the name of a HTTP header and its value being that HTTP header's accompanying value.
  2. If ssdp device does not contain at least one NTS, USN and Location parameter, then the user agent must abort these steps.
  3. If the first occurrence of NTS has a value other than ssdp:alive, then continue to the step labeled update service monitor below.
  4. Let root device descriptor file contain the contents of the file located at the URL provided in the first occurrence of Location obtained according to the rules defined in the section 'Retrieving a description using HTTP' [UPNP].
  5. If root device descriptor file is empty, then the user agent must abort these steps.
  6. Let advertised services be a list of all advertised services obtained from the root device descriptor file containing all sub-nodes of the serviceList node as described in the section 'Device Description' [UPNP].
  7. [eventsURL can not be used. It requires a callback that cannot be set by Javascript. The user agent will be responsible for setting up an event server and requesting 'SUBSCRIBE']
    For each Object advertised service in advertised services run the following steps:
    1. Let network service record be an Object consisting of the following empty properties: id, name, type, url, eventsUrl, config.
    2. Set network service record's id property to the string value of the first occurrence of the USN property.
    3. Set network service record's name property to the string value of the first occurrence of the service's serviceId property.
    4. Set network service record's type property to the string value of the first occurrence of the service's serviceType property.
    5. Set network service record's url property to the string value of the first occurrence of the service's controlURL property.
    6. Set network service record's config property to the string value of the first occurrence of the device property.
    7. If service's eventSubURL property is empty, then continue to the step labeled register below.
    8. Set network service record's eventsUrl property to the string value of the first occurrence of the service's eventSubURL property.
    9. Register: For each Object existing service record in the current list of available service records, run the following sub-steps:
      1. If the existing service record's id property matches the value of the first occurrence of USN and the existing service record's type property matches the value of network service record's type, then set the value of existing service record in the current list of available service records to the value of the network service record and skip the next step.
    10. Add network service record to the list of available service records.
  8. Update Service Monitor: For each non-garbage collected NetworkService object run the following steps:
    1. If this NetworkService object was created from a type attribute but that attribute does not equal the current network service record's type property then continue at the next available active NetworkService object.
    2. If the announcement type equals ssdp:alive then Increment the servicesAvailable attribute of the NetworkServices object by 1. Otherwise, decrement the servicesAvailable attribute of the NetworkServices object by 1.

A user-agent generated callback url is a Local-network accessible URL endpoint that a user agent must generate and maintain for receiving HTTP NOTIFY requests from UPnP Event sources.

When the user agent is to setup a UPnP Events Subscription, it is to run the following steps with the current network service record object:

  1. If network service record's eventsUrl property is empty then the user agent must abort these steps.
  2. Let callback URL be the value of creating a new user-agent generated callback url.
  3. Send a HTTP SUBSCRIBE request with a NT header with a string value of upnp:event, a TIMEOUT header with an integer value of 86400 and a CALLBACK header with a string value of callback URL towards the network service record's eventsUrl property.
  4. If a non-200 OK response is received from the HTTP SUBSCRIBE request then the user agent must abort these steps.
  5. On receiving a valid 200 OK response, run the following steps:
    1. Let callback ID equal the string value of the first included SID header, if it exists.
    2. Let timeout date equal the sum of the current UTC date value plus the integer value of the first included TIMEOUT header, if it exists.
    3. Run the following steps aynchronously and continue to the step labeled listen below.
    4. Refresh Subscription: Run the following steps at a set interval (X) within the user agent:
      1. Let current date equal the current UTC date.
      2. If current date is less than the timeout date then continue to the step labeled refresh subscription above.
      3. Send a HTTP SUBSCRIBE request with a SID header with the string value of callback ID and a TIMEOUT header with an integer value of 86400 towards the network service record's eventsUrl property.
      4. On receiving a valid 200 OK, update callback ID with the string value of the first included SID header, if it exists. All other HTTP responses should cause the user agent to continue from the step labeled refresh subscription above.
    5. Listen: For each HTTP NOTIFY request received at the callback URL the user agent is to run the following steps:
      1. Let content clone be the result of obtaining the message body of the HTTP NOTIFY request. If content clone is empty, then the user agent must abort these steps.
      2. Create a new message event that uses the MessageEvent interface [WEBMESSAGING], with the name message, which does not bubble, is not cancelable, and has no default action.
      3. Let the data attribute of the event have the DOMString value of content clone.
      4. Queue a task to dispatch the newly created event at the current NetworkService object.

8.3 Network Topology Monitoring

When the user agent detects that the user has dropped from their connected network, then it must run the following steps:

  1. Flush all entries from the list of available service records.
  2. For each NetworkService object currently active in the user agent perform the following steps:
    1. Set the readyState attribute to 2 (UNAVAILABLE).
    2. Create a new readystatechange event that uses the Event interface which does not bubble, is not cancelable, and has no default action.
    3. Queue a task to dispatch the newly created event at the NetworkService object.

When the user agent detects that the user has connected to a new network, then it should run the following steps:

  1. Re-issue an mDNS search and SSDP discovery search and handle the responses according to the processing defined in Section 6: Service Discovery.

9. Garbage collection

A NetworkService object containing a url parameter currently in the entry script origin's URL whitelist must not be garbage collected.

Only when the user navigates away from the current browsing context can NetworkService objects be garbage-collected and records in the entry script origin's URL whitelist be removed.

10. Use Cases and Requirements

This section covers what the requirements are for this API, as well as illustrates some use cases.

A. Examples

This section is non-normative.

This sample code demonstrates the updated APIs.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
    <title>Test DLNA</title>
     
    <script type="text/javascript">
     
        function UPnPServAddCallback()
        {
            window.console.log("UPnPServAddCallback() called...");
        }
     
        function UPnPServRemCallback(narNetworkSrv)
        {
            window.console.log("UPnPServRemCallback() called..." + narNetworkSrv.length);
        }
     
        function zcServAddCallback()
        {
            window.console.log("zcServAddCallback() called...");
        }
     
        function zcServRemCallback(narNetworkSrv)
        {
            window.console.log("zcServRemCallback() called..." + narNetworkSrv.length);
        }
     
        function UPnPEventCallback(UPnPEvent)
        {
            window.console.log("UPnPEventCallback() name: "+UPnPEvent.friendlyName);
        }
     
        function NSCallback(narNetworkSrv)
        {
            var len = narNetworkSrv.length;
            window.console.log("NSCallback() RESP: len = "+len);
     
            if (len>0)
            {
                var srv = narNetworkSrv.item(1);
                window.console.log("NSCallback() urls[1]: "+srv.url);
            }
        }
     
        function ERCallback(err)
        {
            window.console.log("ERCallback(): Error found: "+err.code);
        }
     
        function zcNSCB(narNetworkSrv)
        {
            var len = narNetworkSrv.length;
            window.console.log("zcNSCB() RESP: len = "+len);
             
            if (len>0)
            {
                var srv = narNetworkSrv.item(0);
                window.console.log("zcNSCB():  urls[0]: "+srv.url);
            }
        }
     
        function getNS(type)
        {
            navigator.onNetworkEvent(type, UPnPEventCallback, ERCallback);
            navigator.onNetworkServices(type, UPnPServAddCallback, UPnPServRemCallback, ERCallback);
            navigator.getNetworkServices(type, NSCallback, ERCallback);
        }
     
        function zcGetNS(type)
        {
            navigator.onNetworkServices(type, zcServAddCallback, zcServRemCallback, ERCallback);
            navigator.getNetworkServices(type, zcNSCB, ERCallback);
        }
     
        function xhr()
        {
            var xhr = new XMLHttpRequest();
     
            xhr.open("GET", "data2.xml", true);
            xhr.send(null);
            xhr.onreadystatechange = function(event) {
            if (xhr.readyState == 4) {
            var xml = xhr.responseText;
            alert("XML: "+xml);
        }
    </script>
     
    </head>
     
    <body>
        <h1>Test DLNA</h1>
        <br/>
        <input type='button' value='<acronym title="Universal Plug-and-Play">UPnP</acronym>: getNetworkServices'
            onclick='getNS("upnp:urn:schemas-upnp-org:service:ContentDirectory:1");'>
        <input type='button' value='ZeroConf: getNetworkServices' onclick='zcGetNS("zeroconf:_daap");'>
        <input type='button' value='xhrRequest' onclick='xhr();'>
    </body>
</html>

This sample code exposes a button. When clicked, this button is disabled and the user is prompted to offer a network service. The user may also select multiple network services. When the user has authorized a network service to be connected to the web page then the web page issues a simple command to get a list of all the albums stored on the connected media player service.

The button is re-enabled only when the connected network service disconnects for whatever reason (the service becomes unavailable on the network, the user disconnects from their current network or the user revokes access to the service from the current web page). At this point the user can re-click the button to select a new network service to connect to the web page and the above steps are repeated.

The provided service type identifier and service interaction used in this example is based on the well-defined service type and messaging format supported by the XBMC Media Server.


<input type="button" value="Start" onclick="start()" id="startBtn"/>
<div id="debugconsole"></div>
 
<script>
 var startBtn = document.getElementById('startBtn'),
     debug = document.getElementById('debugconsole');
 
 function start() {
   if(navigator.getNetworkServices) {
      navigator.getNetworkServices('_xbmc-jsonrpc._tcp', gotXBMCService, error);
      startBtn.disabled = true;
   } else {
      debug.innerHTML += "<br>Service Discovery <acronym title="Application Programming Interface">API</acronym> not supported!";
   }
 }
 
 function gotXBMCService(servicesList) {
 
   var services = servicesList.services;
 
// Listen for service disconnect messages
 
   services[0].addEventListener('readystatechange', function ( e ) {
     if(services[0].readyState === services[0].UNAVAILABLE) {
       debug.innerHTML += "<br>" + services[0].name + " disconnected.";
       startBtn.disabled = false;
     }
   }, false);
 
// Send a service message to get albums list (and process the service response)
 
   var svcXhr = new XMLHttpRequest();
   svcXhr.open("POST", services[0].url + "/getAlbums"); // services[0].url and its subresources have been
                                                        // whitelisted for cross-site XHR use in this
                                                        // current browsing context.
 
   svcXhr.setRequestHeader('Content-Type', 'application/json-rpc');
 
   svcXhr.addEventListener('readystatechange', function ( response ) {
     if( response.readyState != 4 || response.status != 200 )
        return;
     debug.innerHTML += "<br>" + services[0].name + " response received: ";
     debug.textContent += JSON.parse(response.responseText);
   }, false);
 
   var svcMsg = [
     { "jsonrpc": "2.0", "method": "AudioLibrary.GetAlbums", "params": { "genreid": -1,
         "artistid": -1, "start": -1, "end": -1 }, "id": "1" }
   ];
 
   svcXhr.send(JSON.stringify(svcMsg));
   debug.innerHTML += "<br>" + services[0].name + " request sent: ";
   debug.textContent += JSON.stringify(svcMsg);
 
 }
 
 function error( err ) {
     debug.innerHTML += "<br>An error occurred obtaining a local network service.";
     startBtn.disabled = false;
 }
</script>

This sample exposes a drop-down list containing a number of common Home-based audio devices. When the user selects an audio device from the list provided, they are prompted to authorize a network service based on the service type requested. The user may also select multiple network services matching the selected service type. In this example, the user selects their make as being Sony and their model as being Bravia S1000 from which the Web page can derive a service type (urn:schemas-upnp-org:device:MediaRenderer:1).

Once the user has authorized the device, the web page sends a simple mute command according to the messaging format supported by the device.


<select name="make" id="make">
  <option selected="selected" disabled="disabled">Select make</option>
  <option>Sony</option>
  <option>Philips</option>
  <option>Alba</option>
</select>
<select name="model" id="model"></select>
<div id="debugconsole"></div>
 
<script>
  var debug = document.getElementById('debugconsole');
 
  var models = {
    "Sony": [
      {"name": "Bravia TV S1000", "servicetype": "urn:schemas-upnp-org:service:RenderingControl:1" },
      {"name": "Bravia TV S2000", "servicetype": "_mediarenderer._http._tcp" },
      {"name": "HiFi WD10", "servicetype": "urn:schemas-upnp-org:service:RenderingControl:1" }
    ],
    "Philips": [ /* ... */ ],
    "Alba": [ /* ... */ ]
  };
 
  var makeEl = document.getElementById("make"),
      modelEl = document.getElementById("model");
 
  makeEl.addEventListener('change', function() {
    modelEl.innerHTML = ""; // reset
    var defaultOption = document.createElement("option");
    defaultOption.textContent = "Select model";
    defaultOption.setAttribute("disabled", "disabled");
    defaultOption.setAttribute("selected", "selected");
    modelEl.appendChild(defaultOption);
    for(var i = 0, l = models[makeEl.value].length; i < l; i++) {
      var option = document.createElement("option");
      option.textContent = models[makeEl.value][i]["name"];
      option.setAttribute("value", models[makeEl.value][i]["servicetype"]);
      modelEl.appendChild(option);
    }
  }, false);
 
  modelEl.addEventListener('change', function() {
    if(navigator.getNetworkServices &&
         modelEl.value == "urn:schemas-upnp-org:service:RenderingControl:1") {
      navigator.getNetworkServices(modelEl.value, successCallback, errorCallback);
    } else if (modelEl.value == "_mediarenderer._http._tcp") {
      debug.innerHTML += "<br>Service type is not supported by this application.";
    } else {
      debug.innerHTML += "<br>Service Discovery <acronym title="Application Programming Interface">API</acronym> not supported!";
    }
  }, false);
</script>
 
<script>
  function successCallback( servicesList ) {
 
  // Listen for service push messages
 
    servicesList.services[0].addEventListener('message', function ( msg ) {
         debug.innerHTML += "<br>" + servicesList.services[0].name + " event received: ";
         debug.textContent += msg.data;
    }, false);
 
 // Send a control signal to mute the service audio
 
    var svcXhr = new XMLHttpRequest();
    svcXhr.open("POST", servicesList.services[0].url); // servicesList.services[0].url and its
                                                       // subresources have been whitelisted for
                                                       // cross-site XHR use in this current
                                                       // browsing context.
 
    svcXhr.setRequestHeader('SOAPAction', 'urn:upnp-org:serviceId:RenderingControl#SetMute');
    svcXhr.setRequestHeader('Content-Type', 'application/xml');
 
    svcXhr.onreadystatechange = function ( response ) {
      if( response.readyState != 4 || response.status != 200 )
        return;
      debug.innerHTML += "<br>" + servicesList.services[0].name + " response received: ";
      debug.textContent += response.responseXML;
    }
 
    // Service messaging to mute the provided service
    var svcMsg = '<?xml version="1.0" encoding="utf-8"?>' +
                 '<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ' +
                   'xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">' +
                   '<s:Body>' +
                     '<u:SetMute xmlns:u="urn:schemas-upnp-org:device:MediaRenderer:1">' +
                       '<InstanceID>0</InstanceID>' +
                       '<Channel>Master</Channel>' +
                       '<DesiredMute>true</DesiredMute>' +
                     '</u:SetMute>' +
                   '</s:Body>' +
                 '</s:Envelope>';
 
    svcXhr.send(svcMsg);
    debug.innerHTML += "<br>" + servicesList.services[0].name + " request sent: ";
    debug.textContent += svcMsg;
  }
 
  function errorCallback( error ) {
    debug.innerHTML += "<br>An error occurred: " + error.code;
  }
</script>

B. Acknowledgements

Thanks are expressed by the editor to the following individuals for their feedback on this specification to date (in alphabetical order):

Lars-Erik Bolstad, Hari G Kumar, Bob Lund, Giuseppe Pascale, Marcin Simonides, Clarke Stevens, Christian Söderström, Mark Vickers, ...

Thanks are also expressed by the editor to the following organizations and groups for their support in producing this specification to date (in alphabetical order):

CableLabs, Opera Software ASA, W3C Device APIs Working Group, W3C Web and TV Interest Group, ...

C. References

C.1 Normative references

[DNS-SD]
S. Cheshire; M. Krochmal. DNS-Based Service Discovery. 27 February 2011. IETF Draft. URL: http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
[DOM-CORE]
Anne van Kesteren; et al. Web DOM Core. W3C Working Draft. (Work in progress.) URL: http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
[HTML5]
Ian Hickson; David Hyatt. HTML 5. 4 March 2010. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2010/WD-html5-20100304/
[MDNS]
S. Cheshire; M. Krochmal. Multicast DNS. 14 February 2011. IETF Draft. URL: http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Internet RFC 2119. URL: http://www.ietf.org/rfc/rfc2119.txt
[UPNP]
UPnP Forum. UPnP Device Architecture. Version 1.1.. URL: http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
[WEBIDL]
Cameron McCormack. Web IDL. 19 December 2008. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2008/WD-WebIDL-20081219
[WEBMESSAGING]
Ian Hickson. HTML5 Web Messaging. W3C Working Draft. (Work in progress.) URL: http://dev.w3.org/html5/postmsg/
[ZEROCONF]
S. Cheshire; B. Aboba; E. Guttman. Dynamic Configuration of IPv4 Link-Local Addresses. May 2005. IETF Draft. URL: http://files.zeroconf.org/rfc3927.txt