Copyright © 2008-09 Oracle, All Rights Reserved.
BITSY enables seamless on-line/off-line access to a programmable cache of HTTP resources.
This section describes the status of this document at the time of its publication. Other documents may supersede this document.
This is a working draft of the Web Interface To Synchronization (BITSY) API. This document was produced by Oracle. Oracle seeks to apply the experience of all those that develop off-line applications to develop standards for Web data synchronization.
Comments on this document are invited and are to be sent to the document editor.
This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This section is non-normative.
Mobile Web applications often encounter seemingly random disconnections or network slow downs, which deteriorates application responsiveness and availability, and, therefore, user experience. Special mobile applications for specific mobile device platforms are developed in a fat client-server architecture to work around this problem and are used with special programs that synchronize data with the server.
BITSY — Web Interface To Synchronization — provides a browser-embedded solution to improve application availability and responsiveness by hoarding and subsequently supplying application data. One primitive of BITSY, introduced by Gears [Gears], is a programmable HTTP cache that can be manipulated by a Web application. The new primitive introduced by BITSY offers applications a way to intercept HTTP requests, e.g., GET or PUT. Using these two primitives, applications can locally cache required data, complete requests to HTTP resources whether or not the requests can be serviced immediately by a remote server, and later replay locally satisfied requests to the server. This enables embedded HTTP serving inside user agents and Web applications, thus simplifying the task of creating applications in low connectivity conditions.
BITSY is in the native style of the Web [WEBARCH]. By native style, we mean the use of URLs, uniform methods of accessing and manipulating data, and hypermedia — an information medium consisting of text, video, audio, graphics, and hyperlinks among them. Even existing applications can continue to use their data the same way whether the data is hoarded in BITSY or whether it is served over the network. New applications can design for improved responsiveness due to local caching of data.
Capture is the process of storing the representation of a resource into a persistent data cache.
A managed resource is one whose HTTP [RFC2616] URL is captured using BITSY and requires programmatic interception.
A static response is one that is generated directly from a captured representation of a resource.
A dynamic response is one that is generated programmatically by using application-supplied logic.
Transparent off-line data access and manipulation means to locally generate a static or a dynamic response for HTTP requests to captured resources.
Secure resources are those that require authorization.
This section is non-normative.
This specification only lays down the expectations of BITSY implementations in terms of syntax and semantics. BITSY can be accessed using a number of different programming languages (e.g., JavaScript) and be delivered as part of various software systems (e.g., Web browser).
BITSY enables a client-side interceptor for HTTP requests to managed resources and allows user agents to process such requests in one of three ways:
The serve policy is only useful for safe HTTP methods, i.e., GET
and HEAD
on resources that do not require interception, whereas the intercept policy can be used for any HTTP method on resources that require interception. Both policies improve availability and responsiveness. However, both may affect data freshness. The review policy is useful for any unsafe HTTP method but can only be used when the user agent is able to communicate over the network. This policy improves data freshness at the cost of reduced responsiveness. User agents may otherwise choose freely from among these options, e.g., using information about the battery condition, network state, or a user preference.
Various binding aspects would be different in different programming languages. For example, certain programming languages such as Java natively offer a Unicode String type. [ECMAScript] applications encode DOMString using UTF-16. Moreover, the client language determines the level of strong typing. So for example, Javascript uses weak typing whereas Java requires strong typing. These aspects are fully illuminated in the normative binding specifications given in the appendices of this specification.
Everything in this specification is normative except for diagrams, examples, notes and sections marked non-normative.
The DOMString type is used in this specification to convey a double byte text string (section 1.1.5 of [DOMCore]).
The key words must, must not, should and may in this document are to be interpreted as described in RFC 2119. [RFC2119]
An implementation must behave as described in this specification in order to be considered conformant .
Implementations may implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.
This specification relies on several underlying specifications.
A conforming implementation must support some version of the HTTP protocol [RFC2616].
In order to protect against attacks, the use of the following headers is prohibited using interfaces defined in this specification.
Accept
Accept-Charset
Accept-Encoding
Accept-Language
Authorization
Cache-Control
Connection
Content-Transfer-Encoding
Cookie
Date
Expect
Host
Keep-Alive
Origin
Range
Referer
Set-Cookie
TE
Trailer
Transfer-Encoding
Upgrade
User-Agent
Via
A conforming implementation must support storage and exchange of cookies as specified by HTTP State Management [RFC2109].
A conforming implementation must define the exception codes defined in [DOM3Core] and referenced in this specification.
This section is non-normative.
This specification introduces a mechanism similar to existing HTTP caches in user agents and provides a means for manipulating resource representations stored in such caches. Applications can store resource representations so that they are included in the normal network access path in user agents, i.e., through page navigation or XMLHttpRequest object [XMLHttpRequest]. This requires applications to enumerate every resource to be captured.
This section is non-normative.
This specification aims to allow a Web application to pre-fetch representations of a set of programmatically determined resources and later serve these representations in response to requests to those resources. It removes the lack of predictability about the availability of cached resources.
The standard HTTP caches built in to existing user agents are under no obligation to locally store a cacheable resource and do not provide any guarantees about off-line serving of HTTP resources.
To address this limitation, this specification introduces the DataCache
object. Web applications can capture resources in to a DataCache
object, which can then be served by the user agent when that resource is requested.
For example, an application can locally store a preference list fetched from its server in the following manner:
var dataCache = ... dataCache.capture('/app/layout.plist');
Later, when any page requests that preference list either through page navigation or an XMLHttpRequest, the user agent can serve it from that data cache:
var req = new XMLHttpRequest; req.open('GET', '/app/layout.plist'); req.onreadystatechange = ... req.send();
An application can obtain a DataCache
object from a Window
or other similar object. An unsecure data cache stores non-secure resources.
Here's how an application can open an unsecure data cache :
var dataCache = window.openDataCache('example');
Applications may also use secure data caches to capture secure resources. Access is restricted by the presence of a previously identified required cookie in the current browsing context. Applications only identify the name of the required cookie, not its value, at the time of opening the cache, whereupon the then current value of the required cookie is recorded in the data cache and used in all future attempts to open that data cache.
An application create a new secure data cache in the following manner:
var dataCache = window.openDataCache('messages', 'SSID');
It is also possible that representations of resources within a single data cache may not be consistent. This inconsistency is not introduced by BITSY, but rather is a manifestation of the non-transactional nature of HTTP. For example, a captured resource may have a hyperlink to an uncaptured resource. Loss of network during periods of such inconsistency would affect the availability of applications using such such data even though user agents should be robust enough to survive intermittent connectivity and restart capture attempts as soon as connectivity is re-established. Therefore, an application should provide status information about off-line readiness to its user and set appropriate expectations about its behavior when network access disappears.
Each data cache holds representations of a set of resources identified by their URLs.
Each origin has an associated set of data caches. Each data cache has a name and an optional authorization cookie used to limit access to data in that cache. There is no way to enumerate the data caches available for an origin from this API or to find the cookie associated with an existing data cache.
The openDataCache(name, cookieName)
method on the Window
and WorkerUtils
interfaces must return a new DataCache
object associated with either:
If a new data cache is created due to a difference between the required cookie of an existing data cache and the cookie present in the current browsing context, then the existing data cache must be destroyed automatically.
More than one data cache may capture a resource. In such a case, the user agent must supply the most recent version of that resource when responding to a request to retrieve the representation of that resource.
DataCache
interfaceinterface DataCache { void capture(in DOMString name, [optional in] DOMString interceptibleMethods, [optional in] CaptureCallback callback); void captureText(in DOMString name, in DOMString content, [optional in] DOMString contentType, [optional in] DOMString interceptibleMethods, [optional in] CaptureCallback callback)); void destroy(); DOMString getHeader(in DOMString url, in DOMString name); DOMString getAllHeaders(in DOMString url); DOMString getText(in DOMString url); bool isCaptured(in DOMString relation); void remove(in DOMString url); }; [Callback=FunctionOnly, NoInterfaceObject] interface CaptureCallback { void handleEvent(in DOMString url, in bool success); };
The representation of a resource identified by the url parameter can be asynchronously captured in a data cache in one of two ways capture(url, interceptibleMethods, callback)
and captureText(url, body, contentType, interceptibleMethods, callback)
. In the first method the user agent asynchronously attempts to obtain the network representation of the identified resource. In the second method, the user agent asynchronously attempts to capture the contents of the body parameter as the entity body of the representation. If the resource exists in the data cache, then the user agent must replace the previously captured representation only if this capture attempt is successful.
If contentType is specified in the captureText
method, then the user agent must use that value for the Content-Type
header of the resource's entity. If no such parameter is present, the user agent must interpret the content as text/plain
.
For either capture
or captureText
method, interceptibleMethods may be optionally specified to enable programmatic interception and dynamic response generation. This parameter consists of a comma separated list of HTTP methods that can be intercepted for local processing. An empty string is allowed for this parameter. If the parameter uses characters disallowed by the Method
production of HTTP [RFC2616], then the method must raise a SYNTAX_ERR
exception. If the resource identified by url exists in the data cache and interceptibleMethods is not provided, then the user agent must retain the existing set of interceptible methods.
If a server keeps a stateful session with the client, it can track the session using cookies [RFC2109]. The user agent should send relevant cookies from its store to a server when capturing a network representation of a resource. If a server sets cookies in response to such a request, the user agent should update its cookie store accordingly.
When the callback parameter is specified, the user agent must invoke it when the capture attempt is completed, whether it is successful or if a network or storage error occurs.
The isCaptured(url)
method must return true if the resource identified by the url has been captured and false otherwise.
The getHeader(url, name)
method must return the value of the header associated with the given name for the captured resource identified by the url. If the resource has not been captured, the method must raise the INVALID_STATE_ERR
exception. If the resource does not exist in the data cache object, then the method must raise the NOT_FOUND_ERR
exception.
The getAllHeaders(url)
method must return all the captured headers for the resource identified by the url. Individual headers must be separated in the returned string by a CRLF sequence. If the resource has not been captured, the method must raise the INVALID_STATE_ERR
exception. If the resource does not exist in the data cache, then the method must raise the NOT_FOUND_ERR
exception.
The getText(url)
method must return the entity body of the captured representation for the resource identified by the url. If the resource has not been captured, the method must raise the INVALID_STATE_ERR
exception. If the resource does not exist in the data cache, then the method must raise the NOT_FOUND_ERR
exception. If the content-type of the resource is not one of text
or application/xml
, then the method must raise the NOT_SUPPORTED_ERR
exception
The remove(url)
method must remove the captured resource identified by the url from the data cache. If the resource does not exist in the data cache, then the method must do nothing.
The destroy()
method must destroy the current data cache, cancel all its in-flight capture attempts, and remove all its captured resources. The user agent may delay the actual reclamation of storage used by the data cache.
To invoke the CaptureCallback
object, a user agent must provide the URL of the captured resource as url and a boolean value success indicating whether the capture attempt was successful.
GET
or HEAD
, on non-interceptible resources. These steps are invoked with an HTTP request called request.
Content-Type
header in the local response using the content type of the required representation.This specification introduces a mechanism similar to existing pluggable protocol handlers, such as [NSURLProtocol], specifically for processing HTTP requests.
This section is non-normative.
This specification enables local processing of arbitrary HTTP operations on managed resources.
Standard user agents do not permit a locally produced programmatic response to HTTP resources, which means that, even if the application wishes, such requests cannot be processed when the server is unreachable.
If the request to capture a resource identifies a set of methods that can be locally satisfied, then the user agent will obtain a dynamic response to requests for that resource for those methods from an interceptor even if the server cannot be reached.
For example, an application that wishes to allow local updates to a locally stored preference list can do the following:
var dataCache = ... dataCache.capture('/app/layout.plist', 'PUT'); window.interceptors['/app/'] = { onintercept: function(interception) { if (...) { // validation fails interception.setStatus(400, 'HTTP/1.1 Bad Request'); interception.send(); return; } var type = interception.getRequestHeader('Content-Type'); dataCache.captureText(interception.requestURL, interception.requestText, type, null, function(url, success) { if (!success) return; // we can't generate a dynamic response interception.setResponseText(interception.requestText); interception.setResponseHeader('Content-Type', type); interception.setStatus(200, 'HTTP/1.1 OK'); interception.send(); }); } };
Alternately, the interceptor can capture the body of a response in some resource and redirect the intercepted request to that resource.
var dataCache = ... dataCache.capture('/app/layout.plist', 'PUT'); window.interceptors['/app/'] = { onintercept: function(interception) { if (...) { // validation fails interception.setStatus(400, 'HTTP/1.1 Bad Request'); interception.send(); return; } dataCache.captureText(interception.requestURL, interception.requestText, interception.getRequestHeader('Content-Type'), null, function(url, success) { if (!success) return; // we can't generate a dynamic response interception.setResponseHeader('Location', interception.requestURL); interception.setStatus(303, 'HTTP/1.1 See Other'); interception.send(); }); } };
Later, when the page updates that preference with an XMLHttpRequest, the user agent responds with a locally produced response by asking the function identified by the onintercept attribute to process the request.
For example, a user agent that prefers updating the server and then notifying the interceptor with results of server updates to a locally stored preference list can do the following:
var dataCache = ... dataCache.capture('/app/layout.plist', 'PUT'); window.interceptors['/app/'] = { onreview: function(interaction) { dataCache.captureText('/app/layout.plist', interaction.responseText, interaction.getResponseHeader('Content-Type')); } };
Later, when the page updates that preference with an XMLHttpRequest, the user agent obtains the server response and then calls the function identified by the onreview attribute to process the response.
interceptors
attributeThe interceptors
attribute on the Window
and WorkerUtils
interfaces represents an associative array of Interceptor
objects. The index of an Interceptor
object is its namespace, which is the path prefix within the origin of the current browsing context that the Interceptor
object can intercept. The effective interceptor for a given URL is the one whose namespace matches the longest prefix of the given URL. There can be at most one Interceptor
object for any given namespace.
interface Interceptor { // properties attribute InterceptHandler onintercept; attribute ReviewHandler onreview; }; [Callback=FunctionOnly, NoInterfaceObject] interface InterceptHandler { void handleEvent(in HttpInterception interception); }; [Callback=FunctionOnly, NoInterfaceObject] interface ReviewHandler { void handleEvent(in HttpInteraction interaction); };
The onintercept
attribute must store the InterceptHandler
that the user agent must invoke for intercepting a request to a managed resource in the namespace of this Interceptor
. The algorithm for intercepting requests is described in Section 3.4.1.
The onreview
attribute must store the ReviewHandler
that the user agent must invoke for propagating response to a managed resource in the namespace of this Interceptor
. The algorithm for propagating response is described in Section 3.4.2.
To invoke the InterceptHandler
object, a user agent must provide an HttpInterception object to allow the application to respond to the intercepted request.
To invoke the ReviewHandler
object, a user agent must provide an HttpInteraction object to allow the application to process the network representation received for the request.
HttpInterception
and HttpInteraction
interfacesinterface HttpRequest { readonly attribute DOMString method; readonly attribute DOMString requestURL; readonly attribute DOMString requestText; readonly attribute DOMString allRequestHeaders; DOMString getRequestHeader(in DOMString name); }; interface HttpInterception : HttpRequest { void setStatus(in unsigned short code, in DOMString text); void setResponseText(in DOMString text); void setResponseHeader(in DOMString name, in DOMString value); void send(); }; interface HttpInteraction : HttpRequest { readonly attribute unsigned short statusCode; readonly attribute DOMString statusLine; readonly attribute DOMString responseText; readonly attribute DOMString allResponseHeaders; DOMString getResponseHeader(in DOMString name); };
The method
attribute represents HTTP method, in upper-case characters, present on an intercepted or reviewed request.
The requestURL
attribute represents the URL of an intercepted or reviewed HTTP request.
The requestText
attribute represents the entity body intercepted or reviewed request.
The allRequestHeaders
attribute represents the sequence of HTTP request and entity headers, separated by a CRLF sequence, of the intercepted or reviewed request.
The getRequestHeader(name)
method must return the value of the header associated with the given name for the intercepted or reviewed request. If no such header is present, this method returns the Null object.
The setResponseHeader(name, value)
method must store the value for the header with the given name for the response of interception. If a previous value is associated with this header, then this method must append the value to it.
The setStatus(code, text)
method must store the numeric status from code and status description from text for the response of interception, replacing any previous values for both.
The setResponseText(text)
method must store the entity body from text on the response of a reviewed request, replacing any previous value.
The send()
method must dispatch the result of interception to the requesting application along with the specified status, headers, and body. No further changes must be allowed to this HttpInterception
object.
The statusCode
attribute represents numeric status code present on the response to a reviewed request.
The statusLine
attribute represents the status text present on the response to a reviewed HTTP request.
The responseText
attribute represents the entity body present on the response of a reviewed request.
The allResponseHeaders
attribute represents the sequence of HTTP response and entity headers, separated by a CRLF sequence, present on the response of a reviewed request.
The getResponseHeader(name)
method must return the value of the header associated with the given name from the response to a reviewed request. If no such header is present, this method returns the Null object.
User agents may employ one of two algorithms when processing HTTP requests.
X-Bypass-BITSY
and the value of that header is true
, then abort the remaining steps and continue processing the request without involving interceptors.Interceptor
object for the requested resource and call it the effective interceptor.Interceptor
object is found, then abort the remaining steps and continue processing the request without involving interceptors.onintercept
attribute, then abort the remaining steps and continue processing the request without involving interceptors.onintercept
attribute of the effective interceptor as interceptor.HttpInterception
object from the current request and call it interception object.X-Bypass-BITSY
and the value of that header is true
, then abort the remaining steps.Interceptor
object for the requested resource and call it the effective interceptor.Interceptor
object is found, then abort the remaining steps.onreview
attribute, then abort the remaining steps.onreview
attribute of the effective interceptor as interceptor.HttpInteraction
object from the original request and the server response and call it interaction object.A BITSY implementation stores data on behalf of a server. A description of the HTTP security mechanisms as applicable to BITSY enabled applications as a combination of encryption and access authentication is provided here.
Applications need to verify the identity of their users before allowing access to secure resources they manage. Typically a server verifies the identity of its users by checking whether the user possesses valid credentials including a shared secret, e.g., a password. Applications design their own user interface to provide a means for users to supply their credentials for authentication. In Web applications, this is typically performed using HTML forms and it provides applications with a great deal of control over the authentication user interface. Also, applications typically do not verify the user's credentials for every request. Instead, applications verify a token stored on the client as a result of authentication. This token is a session identifier often stored in an HTTP cookie [RFC2109]. This approach is highly scalable since just the session identifier and not credentials are validated for every HTTP request. Use of session identifiers gives the data source wide latitude over terminating the authorization and restricting access to certain scopes. It also allows users to share authorization but not their credentials with a variety of less-trustworthy applications.
The token approach also enables off-line authentication without storing any credentials locally. The token produced by the server is used by BITSY to authenticate requests served locally and for capturing resources from the server. If a data cache is to store secure resources, it must be created with a cookie name. Once such a data cache is created, the user agent must serve or intercept requests to its captured resources only if the cookie used to secure the cache is still present in the current browsing context.
Failing this, the user agent must make the request to the server as would be the case if the resource were not captured locally. If the user agent receives a 401 error from a server while capturing a resource using a required cookie, then it must automatically destroy the data cache originating that capture attempt.
// File: bitsy.idl #ifndef _BITSY_IDL_ #define _BITSY_IDL_ #include dom.dll module bitsy { interface DataCache { void capture(in DOMString name, [optional in] DOMString allowedMethodList, [optional in] CaptureCallback callback); void captureText(in DOMString name, in DOMString content, [optional in] DOMString contentType, [optional in] DOMString allowedMethodList, [optional in] CaptureCallback callback)); void destroy(); DOMString getHeader(in DOMString url, in DOMString name); DOMString getAllHeaders(in DOMString url); DOMString getText(in DOMString url); bool isCaptured(in DOMString relation); void remove(in DOMString url); }; [Callback=FunctionOnly, NoInterfaceObject] interface CaptureCallback { void handleEvent(in DOMString url, in bool success); }; interface Interceptor { // properties attribute InterceptHandler onintercept; attribute ReviewHandler onreview; }; [Callback=FunctionOnly, NoInterfaceObject] interface InterceptionHandler { void handleEvent(in HttpInterception interception); }; [Callback=FunctionOnly, NoInterfaceObject] interface ReviewHandler { void handleEvent(in HttpInteraction interaction); }; interface HttpRequest { readonly attribute DOMString method; readonly attribute DOMString requestURL; readonly attribute DOMString requestText; readonly attribute DOMString allRequestHeaders; DOMString getRequestHeader(in DOMString name); }; interface HttpInterception : HttpRequest { void setStatus(in unsigned short code, in DOMString text); void setResponseText(in DOMString text); void setResponseHeader(in DOMString name, in DOMString value); void send(); }; interface HttpInteraction : HttpRequest { readonly attribute DOMString responseText; readonly attribute unsigned short statusCode; readonly attribute DOMString statusLine; readonly attribute DOMString allResponseHeaders; DOMString getResponseHeader(in DOMString name); }; #endif // _BITSY_IDL_
This appendix contains the complete ECMAScript [ECMAScript] binding for the BITSY definitions.
Object DataCache
The DataCache object has the following methods:
capture(url, allowedMethodList, callback)
This method does not returns a value.
The url parameter is of type String.
The optional allowedMethodList parameter is of type String.
The optional callback parameter is of type CaptureCallback.
captureText(url, text, contentType allowedMethodList, callback)
This method does not returns a value.
The url parameter is of type String.
The text parameter is of type String.
The optional contentType parameter is of type String.
The optional allowedMethodList parameter is of type String.
The optional callback parameter is of type CaptureCallback.
destroy()
This method does not return a value.
getHeader(name)
This method returns a String.
The name parameter is of type String.
getAllHeaders()
This method returns a String object.
getText()
This method returns a String object.
isCaptured()
This method returns a Boolean object.
remove(url)
This method does not return a value.
The url parameter is of type String.
Object Interceptor
The DataCache object has the following properties:
onintercept
This property is of type InterceptHandler.
onreview
This property is of type ReviewHandler.
Object HttpRequest
The HttpRequest object has the properties and methods defined below:
The HttpRequest object has the following properties:
method
This read-only property is of type String.
requestURL
This read-only property is of type String.
requestText
This read-only property is of type String.
allRequestHeaders
This read-only property is of type String.
The HttpRequest object has the following methods:
getRequestHeader(name)
This method returns a String object.
The name parameter is of type String.
Object HttpInterception
The HttpInterception object has the methods and properties defined for the HttpRequest object as well as the following methods:
send()
This method does not return a value.
setStatus(code, text)
This method does not return a value.
The code parameter is of type Number.
The text parameter is of type String.
setResponseHeader(name, value)
This method does not return a value.
The name parameter is of type String.
The value parameter is of type String.
setResponseText(text)
This method does not return a value.
The text parameter is of type String.
Object HttpInteraction
The HttpInteraction object has the properties and methods defined for the HttpRequest object as well as the methods and properties defined below:
The HttpRequest object has the following properties:
statusCode
This read-only property is of type Number.
statusLine
This read-only property is of type String.
responseText
This read-only property is of type String.
allResponseHeaders
This read-only property is of type String.
The HttpRequest object has the following methods:
getResponseHeader(name)
This method returns a String object.
The name parameter is of type String.
Object InterceptHandler
This is an ECMAScript function reference. This method has no return value. The parameter is a HttpInterception object.
Object ReviewHandler
This is an ECMAScript function reference. This method has no return value. The parameter is a HttpInteraction object.
Object CaptureCallback
This is an ECMAScript function reference. This method has no return value. The parameters are a String object followed by a Boolean object.