There are three main mechanisms that can be used to load data asyncronously once a page is loaded:
- XMLHTTPRequest (XHR)
- IFRAME
- SCRIPT
All of these work well enough when retrieving data from the same server that served the page (at least in all the browsers under test below), but the problems arise when you want to retireve data from a different host or port, because you run into security restrictions implemented in the browser.
Some other comet implementations include a proxy engine that allows the comet server to handle all the requests directly, and pass non-comet requests through to a hidden web server, but Meteor does not support that style of behaviour, and there are some very good reasons to avoid doing it in this way.
The behaviours
All these results come from observations using these tests.
| T'port | Configuration | IE 6/7 | FF 2 | Op 9 | Saf 2 | Saf 3 | iPhone |
|---|---|---|---|---|---|---|---|
| XHR | one-part, same-origin | Yes | Yes | Yes | Yes | Yes | Yes |
| one-part, same host, different port | Yes | No | No | No | No | No | |
| one-part, parent host, same port | No | No | Yes2 | No | No | No | |
| one-part, parent host, different port | No | No | No | No | No | No | |
| incremental, same-origin | No8 | Yes | No9 | No3 | No3 | Yes | |
| incremental, same-origin, 1K prepended 'noise' | No8 | Yes | No9 | Yes | Yes | Yes | |
| incremental, same host, different port | No8 | No | No9 | No | No | No | |
| incremental, same host, different port, 1K prepended 'noise' | No8 | No | No9 | No | No | No | |
| incremental, parent host, same port | No | No | No9 | No | No | No | |
| incremental, parent host, same port, 1K prepended 'noise' | No | No | No9 | No | No | No | |
| incremental, parent host, different port | No | No | No | No | No | No | |
| incremental, parent host, different port, 1K prepended 'noise' | No | No | No | No | No | No | |
| T'port | Configuration | IE 6/7 | FF 2 | Op 9 | Saf 2 | Saf 3 | iPhone |
| IFRAME | one-part, same-origin | Yes1 | Yes1 | Yes1 | Yes1 | Yes | Yes |
| one-part, same host, different port | Yes1 | Yes1 | No | No | Yes1 | No | |
| one-part, parent host, same port | Yes1 | Yes1 | Yes1 | Yes1 | Yes1 | Yes1 | |
| one-part, parent host, different port | Yes1 | Yes1 | No | No | Yes1 | No | |
| incremental, same-origin | No3 | Yes1 | Yes1 | No3 | No3 | Yes | |
| incremental, same-origin, 1K prepended 'noise' | Yes1 | Yes1 | Yes1 | Yes | Yes | Yes | |
| incremental, same host, different port | No3 | Yes1 | No | No | No3 | No | |
| incremental, same host, different port, 1K prepended 'noise' | Yes1 | Yes1 | No | No | Yes1 | No | |
| incremental, parent host, same port | No3 | Yes1 | Yes1 | No3 | No3 | Yes | |
| incremental, parent host, same port, 1K prepended 'noise' | Yes1 | Yes1 | Yes1 | Yes1 | Yes1 | Yes | |
| incremental, parent host, different port | No3 | Yes1 | No | No | No3 | No | |
| incremental, parent host, different port, 1K prepended 'noise' | Yes1 | Yes1 | No | No | Yes1 | No | |
| T'port | Configuration | IE 6/7 | FF 2 | Op 9 | Saf 2 | Saf 3 | iPhone |
| SCRIPT | one-part, same-origin | Yes | Yes | Yes | Yes | Yes | Yes |
| one-part, same host, different port | Yes | Yes | Yes | No | Yes | No | |
| one-part, parent host, same port | Yes | Yes | Yes | Yes | Yes | Yes | |
| one-part, parent host, different port | Yes | Yes | Yes | No | Yes | No | |
| any incremental-loading configuration | No | No | No | No | No | No | |
| Shorten document.domain | Yes | Yes | Yes | Yes | Yes | Yes | |
| Lengthen document.domain to original length once shortened | Yes | No | No | No7 | No7 | No7 | |
| T'port | Configuration | IE 6/7 | FF 2 | Op 9 | Saf 2 | Saf 3 | iPhone |
Table notes
- IFRAME can only be accessed if its document.domain matches the parent frame's document.domain
- XHR can only be made if document.domain is a match for or a parent of the target host. This will always be the case for XHRs to the originating host, but document.domain must be shortened to allow XHR to the parent domain.
- Safari and IE have a buffer which must fill up before any response is parsed. Data received before the buffer is full will not be rendered or interpreted and will not fire the Interactive state of an XHR until the buffer is full or the connection is closed. The obvious solution to this is to send a blob of 'noise' at the start of any response that needs to be parsed incrementally. The size of the buffer that needs to be filled is 512 bytes in Internet Explorer, and between 256 bytes and 1K in Safari depending on the Content-type of the response (a non-standard content-type equals a 256b buffer, while a text/html content type may be as much as 1K)
- SCRIPT loading is a blocking action in Opera, non-blocking in other browsers.
- In Safari, HTTP URLs that do not explicitly define a port number (and therefore default to port 80 in all browsers) are considered to have a different port to those URLs that do explicity define port 80, even though both URLs actually use the same port. The test suite therefore defines 'same port' as 'no port specified', rather than explicitly setting :80.
- Despite setting cache-busting headers, you must give every script you load using the SCRIPT transport a unique URL, else the browser may simply pull the existing script out of memory and run it again.
- Fails silently, does not throw an error
- In Internet Explorer, the first incremental response to XHRs does fire a readyState 3 'interactive' event, but neither the body nor the headers of the response are available until the response is complete, making these events essentially useless. So for the purposes of incrementally downloading data into the browser, the incremental XHR is considered non-functional in IE.
- Although the
responseTextproperty of the XHR is available while the response is still being received, Opera only fires the readyState 3 'interactive' event once, so subsequent packets received will not be processed.
The tests
If you want to see the results for a different browser that's not listed above, go run the tests yourself and contribute the results, we'll add them to the table above.
Conclusions
The way Meteor is designed to work is compatible with all of these browsers, in that a subdomain frame can talk to a parent domain frame, and all actual server conenctions can be made to same-origin from the subdomain frame. Separating the comet server from the web server using ports won't work in Safari 2 or Opera.