I am developing a Perl chat server designed to push events (JS commands) back to the browser for real-time updates and reduced server load from polling. Currently I am at a dead stop with IE. I am using a Comet implementation modified slightly from this page: http://empireenterprises.com/_comet.html

I am looking for anyone who knows why IE is not executing the Javascript commands sent by the server (alert('a' + 0) for example) when they are loaded in an iframe (and even when they are not loaded in an iframe).

The browser connects to the push server via a long-lived XMLHTTPRequest (Firefox/Opera) or an htmlfile ActiveXObject (IE) as seen in the example. The server stores the HTTP browser socket connection and sends commands back to it for each of the chat channels that it is subscribed to. This works fine in Opera and Firefox, but IE does not handle the streaming properly.

I included a very simple, self-contained, and well-documented implementation of the push technology used at the link above. The script is a simple Perl HTTP server listening on port 9090 (this is a very simple mimic of the environment of my actual application). The primary difference is that my server is an HTTP server, not a mod_perl script (using persistent Apache connections for this application is not acceptable). Here are my instructions for the provided code and what I've tried so far:

Once the Perl command line is running the script below, connect to http://localhost:9090 in Firefox. The Perl HTTP server will print the page which will then connect back to the HTTP server (via http://localhost:9090/?comet) and stream the content. The content generated by the server is three simple alert() calls which each include a 'a', 'b', or 'c' and random number for reference, these are also shown in the server command line. Firefox will print these messages as they are generated by the server, but IE will not print them until the server closes the socket and the full DOM has been constructed.

The most curious thing I have found is that if you go to http://localhost:9090/?comet in IE, the first time the alert()s will not show up until the script has finished. However, if you refresh the page, the alert()s will come in at the proper time. I am hoping that this is a good signal to someone of what I am doing wrong.

Have I implemented this sample HTTP server incorrectly? Am I printing something wrong? Am I completely off track?

I've tried using $client->send() instead of print $client, sending w/ and w/o endlines, sending the OK header and not, sending <html><body> before the script tags, along with many other miscellaneous ideas and combinations, but maybe just not quite the right one.

Many thanks to anyone who can take a look at this and help figure out the problem!
- Ian Paterson

Code...
#!/usr/bin/perl -- # IE streaming server-push problem demonstration ##################### +########## ###################################################################### +########## # This script is meant to demonstrate strange IE behavior when working + with # streaming data. When directed to http://localhost:9090/, the browse +r should # print a page which simply displays the following: # # Awaiting commands... # Connecting... (script should start alerting soon) # Executing command: alert('a' + 0) # Executing command: alert('b' + 0) # Executing command: alert('c' + 1) # Disconnected. # Connecting... (script should start alerting soon) # # Each command should be accompanied by an alert box with the specifie +d message. # The script should print these messages in Firefox, but in Internet E +xplorer, # the printing of Executing command: is not expected to occur since IE + is not # expected to allow access to the responseText until the server has cl +osed the # socket. It /should/, however, signal alerts, but this is not happen +ing. # # After the server prints the three commands, it closes the socket. T +he # client re-establishes the connection and it begins again. Only at t +his point # does IE print the alert messages. use IO::Socket; use strict; $| = 1; my $sock = new IO::Socket::INET( LocalPort => 9090, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ); while (my $client = $sock->accept) { print "\nReceived a connection\n"; my $request = <$client>; # PRINT THE COMET SCRIPT SNIPPETS ################################## +########## # This code is run when connecting to localhost:9090/?ajax if ($request =~ /comet/) { print $client "HTTP/1.0 200 OK\nContent-type: text/html\n\n"; $_ = int(rand() * 5); print "a $_\n"; print $client "<script>alert('a ' + $_)</script>\n"; sleep(2); # wait 2 seconds $_ = int(rand() * 5); print "b $_\n"; print $client "<script>alert('b ' + $_)</script>\n"; sleep(2); # wait 2 seconds $_ = int(rand() * 5); print "c $_\n"; print $client "<script>alert('c ' + $_)</script>\n"; } # PRINT THE HTML PAGE ############################################## +########## # This code is run when connecting to localhost:9090 else { print $client "HTTP/1.0 200 OK\nContent-type: text/html\n\n"; print $client qq{ <html> <body> <input type="button" value="Stop connection attempts" onclick="recon +nect = 0;"> <div id="status"> Awaiting commands...<br> </div> <script> // Change the server address here if you cannot use localhost:9090 var server = 'http://localhost:9090/'; var interval = 0; var status = document.getElementById('status'); var pos = 0; var reconnect = 1; // Adapted from http://empireenterprises.com/_comet.html // Removed Opera support for this demo function connect() { status.innerHTML += 'Connecting... (script should start alerting + soon)<br>'; pos = 0; // Internet Explorer (just a quick IE vs FF check) if (document.all) { // By creating a new ActiveXObject('htmlfile'), there is no lo +ad bar. var doc = new ActiveXObject('htmlfile'); doc.open(); doc.write('<html>'); doc.write('<html>'); doc.write('<script>document.domain = \\'' + document.domain + +'\\''); doc.write('</html>'); doc.close(); var div = doc.createElement('DIV'); doc.appendChild(div); // This /should/ just run the Javascript commands as it receiv +es them, // but unfortunately it is not. div.innerHTML = '<iframe name=\\'ifr\\' src=\\'' + server + '/ +?comet\\'></iframe>'; // It should not be useful to poll the iframe. From what I un +derstand // about IE, the content WILL NOT be available until the DOM i +s // complete. Unfortunately that doesn't happen when streaming +. // Hence, the messages are printed as <script> tags because I // believe that somehow (perhaps if the message is printed /ju +st right/) // that IE will actually call the Javascript functions as it r +eceives // them. // Nevertheless, I am leaving this code in because it is also +in the // example. if(!interval) { interval = setInterval( function() { var xmlhttp = doc.frames['ifr'].document; if(xmlhttp.readyState == 'complete') { clearInterval(interval); interval = false; status.innerHTML += 'Disconnected.<br>'; if (reconnect) connect(); } var data; try { data = xmlhttp.firstChild.innerHTML; } catch(error) { return; } data = data.substring(pos); if(data.indexOf('<SCRIPT>') < 0) { return; } var start = data.indexOf('<SCRIPT>') + 8; var stop = data.indexOf('<\\/SCRIPT>') - start; data = data.substr(start, stop); try { // Uncomment these two lines to see that IE only recei +ves the // innerHTML after the server closes the socket. //status.innerHTML += '&nbsp; &nbsp; Executing command +: ' + data.replace(/</g,'&lt;') + '<br>'; //eval(data); } catch(error) { } pos += start + 8 + stop - 1; }, 500 ); } } // Firefox. Opera implementation works, too - it just isn't inc +luded // (use an interval to poll xmlhttp.responseText) else { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if(xmlhttp.readyState == 4) { status.innerHTML += 'Disconnected.<br>'; if (reconnect) connect(); return false; } if(xmlhttp.readyState == 3) { var data = xmlhttp.responseText; data = data.substr(pos); if(data.indexOf('<script>') < 0) { return(false); } var start = data.indexOf('<script>') + 8; var stop = data.indexOf('<\\/script>') - start; data = data.substr(start, stop); status.innerHTML += '&nbsp; &nbsp; Executing command: ' ++ data.replace(/</g,'&lt;') + '<br>'; try { eval(data); } catch(error) { } pos += start + 8 + stop; } }; xmlhttp.open('GET', server + '/?comet', true); xmlhttp.send(''); } } connect(); </script> </body> </html> }; close o; } close $client; } 1;

In reply to Streaming JS commands to IE with a Perl HTTP push server by freefall

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.