Meteor

An HTTP server for the 2.0 web

Welcome to Meteor - you're a star! (Learn more)
This is a description of the various methods Meteor uses in the browser to interpret responses from the Meteor server. As usual it's a hotch-potch of browser incompatibilties, but thankfully there's something that works for pretty much every combination.

Any requirement to load additional data asynchronously into a page once the page has loaded requires making a new HTTP connection. The common pitfalls of such an action are:

  1. 'Click' navigation noises in Internet Explorer
  2. An unwanted entry in the browser history preventing use of the 'Back' button
  3. Inability to access the data until it has finished loading
  4. 'Loading'-type effects, like progress bars and spinners
  5. Cross site scripting security restrictions

And the methods, or transports, available are:

The techniques described here are those employed by Meteor's Javascript client to achieve the best result in each browser without any of the issues documented at the top of this page, and use a combination of several of the transports listed above. Details of the browser capability tests that led to these techniques being developed are documented in the cross site scripting matrix.

Forever Frame via HTMLFile ActiveX Object

Useful for: Streaming in MSIE

The IFRAME method normally produces navigation clicks and 'loading' actions in IE, so to get around this we can take advantage of a nifty IE-only solution - the HTMLFile ActiveX object, which must have been dreamt up by someone who has never heard of standards. Not that we're complaining. This neat solution essentially creates an in-memory page, in which you can, conveniently, have an IFRAME that's dynamically loading something. Here's how it works:

The page loading in the hidden iframe is incrementally loading and executing a set of function calls, and can pass its instructions up to the parent page. Notice that a reference to the Meteor class is passed into the HTMLFile 'page'.

The IFRAME code itself is sent to the browser by Meteor server, which, depending on how it's configured, will likely send something like this:

Every time a <script> chunk is received, the javascript is evaluated, exeuting the local p() function, which is actually a link to the process function in the parent frame (a link established by the parent.Meteor.register() function call).

XMLHTTPRequest Interactive State

Useful for: Streaming in Firefox and Safari

The XMLHTTPRequest in these browsers supports the interactive readyState, and fires events every time a lump of data is received.

To ensure we can make an XHR to the meteor server on a subdomain, we need to issue the XHR from the same host, so we still load an IFRAME, but the page loaded into the frame is simply a static page of Javascript that holds and manipulates the XMLHTTPRequest object that will connect to Meteor for the data stream. This page is served by Meteor so it satisfies the strict same origin policy for XHR, but can also talk to the parent frame thanks to a looser same origin policy for frames (if you're going cross eyed at this point, refer to the cross site scripting matrix).

The magic happens between lines 8 and 29. First we get the latest contents of the receive buffer, which will contain a page similar to the one shown in the forever frame techniques. However, rather than it being loaded and processed incrementally as a page in its own right, we're now going to just treat it as pure data. So we ignore all the header HTML and functions, and start hunting for the <script>p( sections. Having found some (and there might be several new ones on every interactive event), we eval() them one at a time.

Normal XMLHTTPRequest

Useful for: Polling in all browsers

You can implement polling using an XMLHTTPRequest in all browsers, as you don't need to have access to the data stream until the response is complete. To circumvent cross site scripting restrictions (the AJAX target being on a different host), we'll operate the polling client from an IFRAME whose source is loaded using Meteor's static page serving feature. Here's the relevant bit of code from the IFRAME:

The code is run repeatedly at regular intervals using a standard setTimeOut technique. Before the start of every request, document.domain is set to the iframe's domain (ie. Meteor's hostname), eg. push.example.com; then, before dealing with the data retrieved from the response, the domain is set back to the top level domain that the IFRAME and the parent page share, ie. example.com. This is because in Internet Explorer, XMLHTTPRequests can only access URLs on the exact host from which the containing page was requested, while inter-frame data exchange can only happen when both pages share the same document.domain. So it needs to be changed every time we make a request. The setting of the document.domain parameter is in a try...catch enclosure because it will fail in Firefox, which only allows document.domain to be shortened. However, this doesn't matter, because Firefox will in any case allow XMLHTTPRequests despite document.domain being set to a host that does not match the target of the request.

To avoid back-button issues, we'll load this polling iframe into an HTMLFile in IE, and a double IFRAME in Firefox and Safari.

Simple IFRAME Forever Frame

Useful for: Streaming in Opera

The IFRAME method is the best Meteor can do in Opera, which supports neither the XHR Interactive readystate, nor the HTMLFile activeX object. So this method continues to suffer from loading effects. However, Opera does support "server-sent events", which we hope to add to Meteor soon.