6

As of 2 days ago, nginx started to support websocket connections, therefore I was trying to get my nginx-nodejs-socket.io application to work without HAproxy ect (not much luck though).

What I want exactly to achieve is nginx to send only websocket connection requests to a backed server,or websocket server, socket.io to be more exact, while in the same time nginx will be serving php files, and all static content including html files.I don't want express to serve static content at all (if this is possible).

Here is my nginx.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;

    upstream backend {
        server 127.0.0.1:8080;
    }

    server {
        listen       80;
        server_name  localhost;

        charset UTF-8;

        #access_log  logs/host.access.log  main;

        location / {
            root   /website/html_public;
            index  index.php index.html index.htm;
        }       
        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /website/html_public;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
        root           /website/html_public;
        try_files      $uri =404;
        fastcgi_pass   unix:/tmp/php5-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        location ~ /\.ht {
            deny  all;
        }

         location /connection {
         proxy_pass http://backend;  
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
             }
       }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   /website/html_public;
    #        index  index.php index.html index.htm;
    #    }
    #}
}

And here is my server.js file in node

var express = require('express');
var app     = express();
var port    = 8080;

/* HTTP Server*/

server = require('http').createServer(app);
server.listen(port);
app.use(express.logger(':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'));
app.use(express.static(__dirname + '/html_public'));
app.use(express.favicon());

app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.set('views', __dirname + '/views');

app.get('/', function(req, res){
    res.render('index.html');

});

/*
* Web Sockets
*/
io   = require('socket.io').listen(server),
io.configure('production', function(){
io.enable('browser client etag');
io.set('log level', 1);
io.set('transports', [ 'websocket', 'htmlfile', 'xhr-polling', 'jsonp-polling' ]);
});


console.log('Chat Server started with Node '+ process.version +', platform '+ process.platform + 'to port %d',port); 

From my client, try to connect like this :

socket = new io.connect('http://localhost/connection');

The problem is that when I try to connect normally, typing localhost in the chrome browser, I see on console:

GET http://localhost/socket.io/socket.io.js 404 (Not Found)

And also when type in the browser : http://localhost/connection I receive "Cannot GET /connection" which is telling me that nginx is not proxing websockets normally with my current configuration.

masegaloeh
  • 17,978
  • 9
  • 56
  • 104
Skeptic
  • 191
  • 1
  • 1
  • 6

2 Answers2

4

OK websockets through nginx working here"

I changed my location section to:

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://backend;
        proxy_redirect off;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

Now it makes very fast web socket connections. Yipee!

user162445
  • 71
  • 1
3

socket.io is a bit sneaky in that it serves up the client script socket.io.js automatically from somewhere in it's node_modules directory.

So what you have to do is tell nginx to pass requests for that location to your node.js as well. In my nginx.conf I have:

    location /chat {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /socket.io {
        proxy_pass http://backend;
    }

However that only gets me so far as then the websocket connection then fails with a 502 "bad gateway" error.

After that socket.io falls back on xhr-polling which is very, very slow.

user162445
  • 71
  • 1
  • Thanks it worked nicely! However i have a small problem with internet explored (ofcourse...),for some reason which i dont get IE falls back to xhr-polling , but when i try to connect from the adress bar like this -> localhost:8080 socket.io is using htmlfile transport. So i dont understand this behavior cause when it falls back to xhr-polling i receive in console : debug - xhr-polling writing 8:: debug - set close timeout for client _Gz18AtRdqUFnHD--9D_ debug - xhr-polling closed due to exceeded duration – Skeptic Mar 01 '13 at 22:18
  • I would prefer IE to use htmlfile transport instead of xhr-polling. Any idea for the reason of transport switching when using or not port 8080 in the adress bar? – Skeptic Mar 01 '13 at 22:29
  • I found a work around for the problem i stated above.Using flashsocket in server.js , problem seems to be solved.Well in IE9 only.In IE8 and 7 is out of the question... – Skeptic Mar 01 '13 at 23:08