08-12-2009, 03:49 AM
In a few of my tests, I found that:
So after doing a few different tests, I came to the conclusion that my HTTP Streaming should be implemented as such:
1) A hidden IFRAME is added dynamically to the page with an event handler on the OnLoad event to signal when the server closed the connection so that we can start it back up again.
2) The HTTP request that is sent via the IFRAME is a GET method with query string that lets the server know what the method is in the parent document that we will be calling.
3) Whenever data is to be sent to the server (as a POST) we will use XMLHTTPRequest to post the data and since we don't care what the response is (other than a non 200 HTTP response code), there isn't anything else we'll need to do.
There are a few things that needs to be reviewed further:
1) How long can the hidden IFRAME remain open for streaming before we start hitting performance and memory issues? 1 minute? 10MBs? These are all settings that can be modified at the server level or possibly be sent by the client in the request from the IFRAME.
2) Since there can be a delay in when data gets to the client from the server and anything can happen between the time the server sent the data versus when the client received and processed it (i.e. the user clicked a button that aborted the stream unexpectedly), the server should maintain a rolling history of past messages. Each message would be index and as they are sent to the client, those indexes are included. This way, a client can request a stream to start at a message by index to avoid losing any messages along the way when it restarts a stream.
Feel free to comment and/or add your thoughts to this.
- The HTTP Response should include the HTTP header Transfer-Encoding: chunked
When the HTTP 1.1 standard was released, Transfer-Encoding was expanded to allow for 'chunked' responses. Normally, when a client (i.e. your browser) requests a resource (such as an HTML page or an image), the server sends the complete content back to the client. The size of this resource (which is defined through the HTTP header Content-Length) is already known to the server. However, it became apparent during the first few years of HTTP 1.0 that the size of a resource may not always be known and the server needs a way of sending blocks of data to the client as they are received. This is where Transfer-Encoding: chunked comes into play. The way I do HTTP Streaming is by sending all my HTTP Headers first and then sending the Transfer-Encoding: chunked header next. At this point, I store the client connection object somewhere and exit out of my response method.
As data comes in that needs to be sent to the client, I grab the connection that I stored and send a string in the following format:
----------
<content size in hex format>
<content>
----------
Please note that the first line is an empty line on purpose and that every line must end with a CRLF combination.
When the server wants to let the client know there is no data left to send, it simply sends the following string:
----------
0
----------
Again, the lines are left blank on purpose and must end with CRLF. - XMLHTTPRequest works with streaming, sometimes...
In Firefox, XMLHTTPRequest works perfectly fine because whenever new data is sent, the onreadystatechange event is always fired. However, in IE7, this is not the case. Also, certain versions of IE have ActiveX versions instead of native browser versions so if the browser has disabled ActiveX, then XMLHTTPRequest won't work in this case either. But fear not, there is another solution that I actually find works better than XMLHTTPRequest for most of my Streaming tests
Let's just say, though, that XMLHTTPRequest did work consistently among the main browsers. There is another annoyances to using XMLHTTPRequest: You have to poll/parse the data every time new data is sent down. Unlike the hidden IFRAME method (which will be described below), when new data is received, you'll have to find it yourself as there is no built-in mechanism to do it for already. While it's a fairly simple chunk of code to do this, it's just something else you'll need to implement.
However, XMLHTTPRequest isn't without it's merits. Native support for being able to set HTTP headers and to get the HTTP headers for the response as well as the status code of a request is a big plus. While there are work-arounds in the hidden IFRAME method, it's something you'll need to implement yourself. - The hidden IFRAME works all the time
Funny, but probably the oldest way of streaming HTTP also works the best. It is supported in all browsers with fairly consistent behaviors. Don't needs to poll the data as it comes in if you use <SCRIPT> tags around your messages.
There is this interesting behavior with browsers. As they get completed SCRIPT tags (tags that are open and closed properly), they try to process them immediately. So HTTP streaming can be implemented as a series of <SCRIPT>---JavaScript code---</SCRIPT> messages sent to the client in a chunked transfer-encoding stream.
Ok, so we are getting these JS snippets executed. How exactly do we incorporate that into our page? What is generally done is that the JS code that is sent is actually a method call to a function defined at either the Window level or the parent document level. It would normally be structured like this:
This is the most common approach, but there are other ways of doing the same thing. Feel free to test them out.Code:
<script type='text/javascript'>window.parent.onServerMessage('some message');</script>
So after doing a few different tests, I came to the conclusion that my HTTP Streaming should be implemented as such:
1) A hidden IFRAME is added dynamically to the page with an event handler on the OnLoad event to signal when the server closed the connection so that we can start it back up again.
2) The HTTP request that is sent via the IFRAME is a GET method with query string that lets the server know what the method is in the parent document that we will be calling.
3) Whenever data is to be sent to the server (as a POST) we will use XMLHTTPRequest to post the data and since we don't care what the response is (other than a non 200 HTTP response code), there isn't anything else we'll need to do.
There are a few things that needs to be reviewed further:
1) How long can the hidden IFRAME remain open for streaming before we start hitting performance and memory issues? 1 minute? 10MBs? These are all settings that can be modified at the server level or possibly be sent by the client in the request from the IFRAME.
2) Since there can be a delay in when data gets to the client from the server and anything can happen between the time the server sent the data versus when the client received and processed it (i.e. the user clicked a button that aborted the stream unexpectedly), the server should maintain a rolling history of past messages. Each message would be index and as they are sent to the client, those indexes are included. This way, a client can request a stream to start at a message by index to avoid losing any messages along the way when it restarts a stream.
Feel free to comment and/or add your thoughts to this.