In contrast to client pull, server push takes advantage of a connection that's held open over multiple responses, so the server can send down more data any time it wants. The obvious major advantage is that the server has total control over when and how often new data is sent down. Also, this method can be more efficient, since new HTTP connections don't have to be opened all the time. The downside is that the open connection consumes a resource on the server side while it's open (only when the server knows it wants this to happen, though). Also, server push has two other advantages: one is that a server push is easily interruptible (you can just hit "Stop" and interrupt the connection), and the other advantage we'll talk about a little later.
First, a short review: the MIME message format is used by HTTP to encapsulate data returned from a server in response to a request. Typically, an HTTP response consists of only a single piece of data. However, MIME has a standard facility for representing many pieces of data in a single message (or HTTP response). This facility uses a standard MIME type called "multipart/mixed"; a multipart/mixed message looks something like:
Content-type: multipart/mixed;boundary=ThisRandomString
--ThisRandomString
Content-type: text/plain
Data for the first object.
--ThisRandomString
Content-type: text/plain
Data for the second and last object.
--ThisRandomString-- |
The above message contains two data blocks, both of type "text/plain". The final two dashes after the last occurrence of "ThisRandomString" indicate that the message is over; there is no more data.
For server push we use a variant of "multipart/mixed" called "multipart/x-mixed-replace". The "x-" indicates this type is experimental. The "replace" indicates that each new data block will cause the previous data block to be replaced -- that is, new data will be displayed instead of (not in addition to) old data.
So here's an example of "multipart/x-mixed-replace" in action:
Content-type: multipart/x-mixed-replace;boundary=ThisRandomString
--ThisRandomString
Content-type: text/plain
Data for the first object.
--ThisRandomString
Content-type: text/plain
Data for the second and last object.
--ThisRandomString--
The key to the use of this technique is that the server does not push the whole "multipart/x-mixed-replace" message down all at once but rather sends down each successive data block whenever it sees fit. The HTTP connection stays open all the time, and the server pushes down new data blocks as rapidly or as infrequently as it wants, and in between data blocks the browser simply sits and waits for more data in the current window. The user can even go off and do other things in other windows; when the server has more data to send, it just pushes another data block down the pipe, and the appropriate window updates itself.
So here's exactly what happens:
• Following in the tradition of the standard "multipart/mixed", "multipart/x-mixed-replace" messages are composed using a unique boundary line that separates each data object. Each data object has its own headers, allowing for an object-specific content type and other information to be specified.
• The specific behavior of "multipart/x-mixed-replace" is that each new data object replaces the previous data object. The browser gets rid of the first data object and instead displays the second data object.
• A "multipart/x-mixed-replace" message doesn't have to end! That is, the server can just keep the connection open forever and send down as many new data objects as it wants. The process will then terminate if the user is no longer displaying that data stream in a browser window or if the browser severs the connection (e.g. the user presses the "Stop" button). We expect this will be the typical way people will use server push.
• The previous document will be cleared and the browser will begin displaying the next document when the "Content-type" header is found, or at the end of the headers otherwise, for a new data block.
• The current data block (document) is considered finished when the next message boundary is found.
• Together, the above two items mean that the server should push down the pipe: a set of headers (most likely including "Content-type"), the data itself, and a separator (message boundary). When the browser sees the separator, it knows to sit still and wait indefinitely for the next data block to arrive.
Putting it all together, here's a Unix shell script that will cause the browser to display a new listing of processes running on a server every 5 seconds:
#!/bin/sh
echo "HTTP/1.0 200"
echo "Content-type: multipart/x-mixed-replace;boundary=---ThisRandomString---"
echo ""
echo "---ThisRandomString---"
while true
do
echo "Content-type: text/html"
echo ""
echo "<h2>Processes on this machine updated every 5 seconds</h2>"
echo "time: "
date
echo "<p>"
echo "<plaintext>"
ps -el
echo "---ThisRandomString---"
sleep 5
done
Note that the boundary is sent to the browser before the sleep statement. This ensures that the browser will flush its buffers and display all the data that's been received up to that point to the user.
Special note to NCSA HTTPD users: You must not use any spaces in your content type, this includes the boundary argument. NCSA HTTPD will only accept a single string with no white space as a content type. If you put any spaces in the line (besides the one right after the colon) any text after the white space will be truncated.
As an example, the following will work:
Content-type: multipart/x-mixed-replace;boundary=ThisRandomString
The following will not work:
Content-type: multipart/x-mixed-replace; boundary=ThisRandomString
THE AFOREMENTIONED OTHER ADVANTAGE TO SERVER PUSH
You can use server push for individual inlined images! Yes, that's right -- you can have a document that contains an image that gets updated by the server on a regular basis or any time the server wants. Just have the SRC attribute of the IMG tag point to an URL for which the server pushes a series of images.
Let's stress this point: if you use server push for an individual inlined image, the image will get replaced inside the document each time a new image is pushed -- the document itself won't get touched (assuming it isn't separately subject to server push).
So this is kind of cool -- poor man's animation inlined into a static document.
EFFICIENCY CONSIDERATION
Server push: generally more efficient, since a new connection doesn't need to be opened for each new piece of data. Since a connection is held open over time, even when no data is being transferred, the server must be willing to accept dedicated allocation of a TCP/IP port, which may be an issue for servers with a sharply limited number of TCP/IP ports.
Client pull: generally less efficient, since a new connection must be opened for each new piece of data. However, no connection is held open over time.
Note that in real world situations it is common for establishment of a new connection to take a significant amount of time -- i.e., one second or more. Given that this is the case, server push will probably be generally preferable for end-user performance reasons, particularly for information that is frequently updated.
Another consideration is that the server has comparatively more control in the server push situation than in the client pull situation. One example: there is one distinct open connection for each instance of server push in use, and the server can elect to (for example) shut down such a connection at any time (e.g., via a cron daemon) without requiring a whole lot of logic in the server. On the other hand, the same application using client pull will look like many independent connections to the server, and the server may need to have a considerable level of complexity in order to manage the situation (e.g., associating client pull requests with particular end users to figure out who to stop sending new "Refresh" headers to).
An Important Note On Server Push And Shell Scripts: If you write a CGI program as a shell script, and the script implements some form of server push where you expect the connection to be open for a long time (e.g. an infinitely long stream of images representing live video), then the shell script normally will not notice when/if the user severs the connection on the client side (e.g., by pressing the "Stop" button) and will continue running. This is bad, as server resources will be thereafter consumed wastefully and uselessly. The easiest way to work around this shell script limitation is to implement such CGI programs using a language like Perl or C -- such programs will terminate properly when the user breaks the connection. |