views:

304

answers:

3

I've been using gimite / web-socket-js to implement a WebSocket past simply Chrome and development builds of Safari. I want to move away from the Ruby server and onto Node.js. Suddenly it doesn't work in anything but Chrome.

I suspect that this has to do with the Flash Socket Policy file that I need to implement. I would like to implement this as an external Node.js process so as not to muddy with the original application. I am using the node-websocket-server for implementing WebSocket protocol with Node.js, and again I would prefer to not mess with that either.

It seemed like the simplest thing to do would be to run flashsocket.js, but running that gives me the following error:

sys:334
    ctor.prototype = Object.create(superCtor.prototype, {
                            ^
TypeError: Object prototype may only be an Object or null
    at Function.create (native)
    at Object.inherits (sys:334:29)
    at Object.<anonymous> (/Users/me/Projects/testing/websocket/node-websocket-server/flashsocket.js:10:16)
    at Module._compile (node.js:472:23)
    at Module._loadScriptSync (node.js:479:10)
    at Module.loadSync (node.js:349:12)
    at Object.runMain (node.js:532:24)
    at node.js:762:10

Here we run into the lovely cryptic errors Node.js is loved for.

My question is is there a stand alone global flash socket policy server I can run off of either Node.js or another application? My understanding is that I only need to have it reside on port 843. Or is there another WebSocket library for Node.js that will handle the Flash Policy like the Ruby server does?

+2  A: 

With a bit of help from the Node.js mailing list I came up with the following:

var net = require("net"),
    domains = ["localhost:8081"];

net.createServer(
    function(socket)
    {
        socket.write("<?xml version=\"1.0\"?>\n");
        socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\"&gt;\n");
        socket.write("<cross-domain-policy>\n");

        domains.forEach(
            function(domain)
            {
                var parts = domain.split(':');
                socket.write("<allow-access-from domain=\""+parts[0]+"\"to-ports=\""+(parts[1]||'80')+"\"/>\n");
            }
        );

        socket.write("</cross-domain-policy>\n");
        socket.end();   
    }
).listen(843);

I also wrote a (brief) tutorial for WebSockets applications using Flash Sockets.

Josh K
A: 

It's better to override Stream's listeners (socket's listeners). Otherwise Your server will crashing when you have some error like:

ECONNRESET, Connection reset by peer

Sample of implementation to prevent it :

socket.setEncoding("utf8");
socket.addListener("end", function () {socket.end();});
socket.addListener("error", function (exception) {socket.end();});
socket.addListener("timeout", function () {socket.end();});
socket.addListener("close", function (had_error) {socket.end();});

See documentation at : http://nodejs.org/api.html (at "net.Stream")

devwafwaf
A: 

Flash policy requests can also be answered inline on the same port as the WebSockets service you are providing. See this change to the Socket.IO node.js module. It adds a connection listen to the server that answers policy server requests on the same port. That way you don't have to run something on port 843 (which usually requires root privileges).

Alternately, you can also run a very simple (2 line) policy request server using socat (assuming you are on a *nix system): http://github.com/kanaka/noVNC/blob/master/docs/flash_policy.txt

Update (response to @Josh K):

It is a common misunderstanding that port 843 is the primary location for flash policy requests and that same-port requests are a fall-back and that they are slower due to timeout. This is probably based on the commonly cited http://www.lightsphere.com/dev/articles/flash_socket_policy.html and also because Adobe's documentation is difficult to track down (and read). Here is the Adobe documentation on their security policy: http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html

In reality port 843 serves a somewhat different purpose from the same port response. Port 843 is for the meta-policy (site-policy). It takes precedence over same-port policies. The administrator can use it to define flash policies for the entire system and can use it to deny non-privileged users from allowing inbound flash socket connections. This is the reason why it is on port 843 (which is in the privileged range) so that only the system administrator can launch the service on that port.

The 3 second timeout only applies in the case where connections to port 843 are silently dropped. It does not apply to the case where there is some other service running on port 843 or the connection is rejected (i.e. TCP reset). I use same-port all the time and there is no perceptible delay with only running a same port policy server.

With a WebSocket server, an additional advantage to the same-port policy response is that you can more easily co-ordinate the origin policy configuration between the flash policy and the WebSockets handshake.

kanaka
@kanaka: Yes, they can be answered inline but that is **not advised** because Flash will check port 843 first. It it doesn't get a response after 3 seconds it will try the actual port. It's better to have a clean FPS running on 843 and your application on it's own port.
Josh K