Socket.IO and IISNode #35

Closed
Dunadan opened this Issue Sep 7, 2011 · 9 comments

Comments

Projects
None yet
7 participants

Dunadan commented Sep 7, 2011

Tomasz, how is it possible to include Socket.IO client library using IIS node?
According to official site recommendations, browser must follow url like http://[daemon]:[port]/socket.io/socket.io.js
For e.g., daemon runs as /server.js with rule

add name="iisnode" path="/server.js" verb="*" modules="iisnode"

I should use a rewrite of some sorts or there is another, better way?

Dunadan commented Sep 8, 2011

Good news:

Just patched /socket.io/lib/socket.io.js (line 52)

Was:
if ('number' == typeof server ) {

NOW:
if ('number' == typeof server || 'string' == typeof server) {

So far Socket.IO server become alive, I see "socket.io started" message in log.

Bad news:
/socket.io.js is not returning to browser. Process is in "wait" state for a while and closes with 500 error.

@Dunadan Dunadan closed this Sep 8, 2011

@Dunadan Dunadan reopened this Sep 8, 2011

Dunadan commented Sep 8, 2011

One more update:
FILE: /node_modules/socket.io/lib/manager.js

LINE: 704 (write function)
Looks like that for some reason Node.Js is unable to put file contents (~100K) into stream.
I know, that this sounds confusing, but it is true.

After some modifications (see below) I was able to see file contents in the log, but not in the browser (.... CONTENT ... string with correct size only).

Without IISNode everything is working perfectly.

My debug modifications:

function write (status, headers, content, encoding) {
//try {
res.writeHead(status, headers || null);
res.write('... CONTENT ' + content.length + ', encoding = ' + encoding + ' ... ');
console.log(content + '');
res.write('[' + content + ']' || '', encoding || null);
res.write('content ends', 'utf-8');
res.end();
//} catch (e) {}
}

function serve () {
if (req.headers['if-none-match'] === cache.Etag) {
return write(304);
}

var mime = static.mime[extension]
  , headers = {
  'Content-Type': mime.contentType
, 'Content-Length': cache.length
};

if (self.enabled('browser client etag') && cache.Etag) {
  headers.Etag = cache.Etag;
}

write(200, headers, cache.content, mime.encoding);
self.log.debug('served static: ' + location);

}

if (this.get('browser client handler')) {
this.get('browser client handler').call(this, req, res);
} else if (!cache) {
data = fs.readFileSync(location);

  var cache = Manager.static.cache[file] = {
    content: data
  , length: data.length
  , Etag: client.version
  };

  serve();

} else {
serve();
}
};

Owner

tjanczuk commented Sep 16, 2011

This was a bug in iisnode code and was fixed with 5ac7bdd. No changes in socket.io code are required. Please note socket.io websocket transport does not work with iisnode since IIS does not support websockets as of this writing. You can use socket.io with the non-websocket transports it offers though:

var http = require('http');
var server = http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/html' });

    // Make the browser issue a request for /socket.io/socket.io.js, and then prove we got it
    res.end('<script src="/socket.io/socket.io.js"></script><script>document.write("Socket IO object: " + window.io)</script>');
});
server.listen(process.env.PORT);

var io = require('socket.io').listen(server);
io.configure(function () { 
  io.set("transports", ["xhr-polling"]); 
  io.set("polling duration", 10); 
});

@tjanczuk tjanczuk closed this Sep 16, 2011

mw1260 commented Jan 27, 2012

This is still a problem for me - is it possible that there is a regression? If I try the code in the previous comment, the variable io is undefined. Which matches the problem that I seem to be having on my app, which is that I can't pull server.io.js through my node endpoint if I use iisnode.

ocombe commented May 8, 2012

Same here, unable to load the socket io since my client is trying to access http://localhost/socket.io/1/?t=1336471906658 but this url is unavailable... do I have to change something to IIS config or to iisnode web.config ?

ocombe commented May 8, 2012

Ok I found the solution, i'm using a different port for the socket.io server (using port 80 will not work since IIS will wait for a connection and serve only /node/...
And since I didn't want to use another port on client side, i'm using http-proxy module to proxy socket.io data

Client side :

<script type='text/javascript' src="/node/push/socket.io/socket.io.js"></script>
<script type='text/javascript'>
    var socket = io.connect('http://localhost', {resource: 'node/push/socket.io'});

    socket.on('msg', function (data) {
        console.log('msg received: ',data);
    });
</script>

Server side :

var app = require('express').createServer(),
    httpProxy = require('http-proxy'),
    proxy = new httpProxy.RoutingProxy(),
    io = require('socket.io').listen(3000);

app.listen(process.env.PORT);

io.configure(function () { 
    io.set("transports", ["xhr-polling"]); // no websockets
    io.set("polling duration", 30); 
    io.set("log level", 1); // no debug msg
});

app.all('/node/push/socket.io/*', function (req, res) {
    req.url = req.url.replace('/node/push', ''); // strip /node/push from url
    proxy.proxyRequest(req, res, {
        host: 'localhost',
        port: 3000
    });
});

io.sockets.on('connection', function (client) {
    console.log('connection');
    client.emit('msg', {
        hello: 'world'
    });
});

I hope this will help people trying to make this work :)

schmod commented Oct 2, 2012

This is totally doable without a proxy or alternate ports! You just need to set the resource parameter on the server.

Here's a very barebones example (without Express). In practice, you'll probably want to use URL rewriting so you can also serve up other content.

On the server (myApp/server.js):

var app = require('http').createServer(handler),
    io = require('socket.io').listen(app);

io.configure(function(){
  io.set('transports',['xhr-polling']); //No Websockets!
  io.set('resource','/myApp/server.js'); //Where we'll listen for connections.  
});

app.listen(process.env.PORT);

function handler(req,res){
  /*  
   *  Your code for handling non-websocket requests...
   *  Note that Socket.IO will intercept any requests made
   *  to myApp/server.js without calling this handler. 
   */
}

io.sockets.on('connection', function (client) {
  console.log("New Connection");

  client.on('message',function(message,callback){
    console.log("Message Received");
    client.emit('response',"Got it!");
  }
}

And on the client:

var socket = io.connect('http://myServer',{resource: 'myApp/server.js'}); //Same resource as the server.
socket.on('response', function (msg) {
  console.log("Message received:",msg);
});
socket.emit('message','hi');

In practice, you'll probably want to set up URL rewriting to rewrite requests for anything inside /myApp/* to server.js. In my case, I rewrite myApp/socket to server.js in web.config, and specify myApp/socket as the resource parameter on both the client and server.

puzit commented Dec 10, 2012

Tomasz, i am able to access Socket.io.js if i run the following code using node.js

var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });

// Make the browser issue a request for /socket.io/socket.io.js, and then prove we got it
res.end('<script src="/socket.io/socket.io.js"></script><script>document.write("Socket IO object: " + window.io)</script>');

});
server.listen(3000);

var io = require('socket.io').listen(server);
io.configure(function () {
io.set("transports", ["xhr-polling"]);
io.set("polling duration", 10);
io.set("resources", '/servernode/server.js')
});

but same code with port as process.env.PORT is not working in IISNode. Please suggest.

this is not working on iis 7.5 ??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment