Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working Behind an (NginX) Proxy? #22

Closed
benjamid opened this issue May 1, 2014 · 24 comments
Closed

Working Behind an (NginX) Proxy? #22

benjamid opened this issue May 1, 2014 · 24 comments

Comments

@benjamid
Copy link

benjamid commented May 1, 2014

First, this has been a great library to use: very convenient and helpful. However, I've had a ton of trouble setting up an NginX (v1.4) reverse proxy, which is needed to handle SSL and other useful functions. Are there any examples of NginX config files that work with this library? I feel like I've tried and modified every socketio .conf variant on the internet at this point, but still can't get the handshake to work. For production use, being able to get this properly proxied is very important.

@mr-pj
Copy link

mr-pj commented May 4, 2014

Did you resolve this issue? I am working on a web application and it will be behind a nginx proxy as well.

What are the errors you are getting? Can you please elaborate more

@dustinmm80
Copy link

The issue may be that gunicorn can't run with more than 1 worker using gevent-socketio. They are discussing it here: abourget/gevent-socketio#132

@miguelgrinberg
Copy link
Owner

In all cases when you have a socket based server you use a single worker. Multiple clients are handled by gevent's greenlets.

I will look into making the example work under nginx and add the config to the documentation.

@b-3-n
Copy link

b-3-n commented May 10, 2014

I'm not sure it is helpful. But it won't hurt anyone: This works for me without SSL (did not test it for SSL):

#proxy for flask

location /flask/ {
        proxy_pass http://127.0.0.1:5000/;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

I got a javascript app for communicating with flask. I had to explicitly define the resource location like so to get it to work:

io.connect('myurl', {resource : flask/socket.io});

Note that I got everything here setup for the url /flask/. Should be trivial to adjust though.

@alcinos
Copy link

alcinos commented May 11, 2014

I've also troubles to have Flask-SocketIO work with Nginx and gunicorn. I keep getting the following Runtime error : "You need to use a gevent-socketio server".

Here is the full traceback :

Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/gevent/pywsgi.py", line 508, in handle_one_response
    self.run_application()
  File "/usr/lib/python2.7/site-packages/gevent/pywsgi.py", line 494, in run_application
    self.result = self.application(self.environ, self.start_response)
  File "/usr/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/python2.7/site-packages/flask_socketio/__init__.py", line 24, in __call__
    raise RuntimeError('You need to use a gevent-socketio server.')
RuntimeError: You need to use a gevent-socketio server.
{'GATEWAY_INTERFACE': 'CGI/1.1',
 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
 'HTTP_ACCEPT_LANGUAGE': 'fr,en-us;q=0.7,en;q=0.3',
 'HTTP_AUTHORIZATION': 'Basic cGVybWl0OmJpZHVsZQ==',
 'HTTP_CONNECTION': 'upgrade',
 'HTTP_COOKIE': 'session=.eJwVy8EKgjAAgOFXiZ0TZkGg4EF0iMkcuU1xFzFNbGsKalAT3z27_j_fCgKfVpyiLPUxAi4Ymnp6jgM4gup_GElQumfKLBue4cWxrSAmtHd0zJtEvMzSEQSZ1Y76bTX1bO-wmaeuWkb1GIC7gsN95-WJ2yLKlYhuX1xwiGWqS5YpbGJTFkKKSGgs4w8Jr0qEeY9NK4nveWDbfpgIMu0.BlEtWg.6NftiiEO0ijupNzkcPBt4gha2zw',
 'HTTP_HOST': 'localhost',
 'HTTP_REFERER': 'http://localhost/planning/2/',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0',
 'HTTP_X_FORWARDED_FOR': '::1',
 'HTTP_X_REAL_IP': '::1',
 'PATH_INFO': '/socket.io/1/210004854639/',
 'QUERY_STRING': 'disconnect=1',
 'RAW_URI': '/socket.io/1/210004854639/?disconnect=1',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_PORT': '47823',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': 'localhost.localdomain',
 'SERVER_PORT': '8000',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'SERVER_SOFTWARE': 'gevent/1.0 Python/2.7',
 'gunicorn.sock': <socket at 0x92efe0c fileno=12 sock=127.0.0.1:8000 peer=127.0.0.1:47823>,
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0xb73480d0>,
 'wsgi.input': <gevent.pywsgi.Input object at 0x92f20ec>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)} failed with RuntimeError

Here are some details about my config :
Nginx :

server {
  listen [::]:80;
  server_name localhost;

  access_log  /var/log/nginx/permit_access.log;
  error_log   /var/log/nginx/permit_error.log;

  location /socket.io {
       proxy_pass         http://127.0.0.1:8000/socket.io;
       proxy_redirect     off;

        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

    # Websockets support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
  location / {
        proxy_pass         http://127.0.0.1:8000/;
        proxy_redirect     off;

       proxy_set_header   Host             $host;
       proxy_set_header   X-Real-IP        $remote_addr;
       proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }
}

js :

var conn_options = {
  'sync disconnect on unload':true,
   resource:'socket.io'
};
   socket = io.connect('http://' + document.domain,conn_options);

gunicorn start command :

gunicorn --worker-class socketio.sgunicorn.GeventSocketIOWorker permit:app

@miguelgrinberg
Copy link
Owner

Do things work when you remove nginx from the equation?

@alcinos
Copy link

alcinos commented May 11, 2014

Yes, if I open directly http://127.0.0.1:8000, everything works smoothly

@alcinos
Copy link

alcinos commented May 11, 2014

However, if I don't use Gunicorn, and run directly the application along with nginx, it doesn't work either.
Code used to run socket-io :

if __name__ == '__main__':
    socketio.run(app,"127.0.0.1",8000)  

and then I run the application directly with python2, and I get the same runtime error

@miguelgrinberg
Copy link
Owner

OK. I haven't really tried to set up nginx to work with socketio, I believe that is where the root of the problem is. As soon as I figure this out I will update the documentation.

@aguegu
Copy link

aguegu commented May 28, 2014

I have tried alcinos's nginx conf. the socketio can not work on port 80. but it works on port 5000.

my run command:
gunicorn --worker-class socketio.sgunicorn.GeventSocketIOWorker -b 0.0.0.0:5000 run_gunicron:app

can i add worker args to it, like "-w 4"?

I have bought Miguel's book. But it does not say much about deployment on trandional hosting. I am trying to build a website on my cloud server, not heroku, but one initiated with just ubuntu server and ssh access. My website would run with flask, flask-socketio, flask-restful, gunicorn, nginx and mongodb. It is about collecting data from arduino with internet chip.

@miguelgrinberg
Copy link
Owner

I have tried alcinos's nginx conf. the socketio can not work on port 80. but it works on port 5000.

Maybe this is a misunderstanding. The normal setup is to put nginx to listen on port 80 to external connections. Nginx then proxies the socket connections to the internal server, which runs on a high port number such as 5000. If this is what you have, then you are doing it right.

The gevent based servers create and destroy workers automatically using greenlets. At the gunicorn level you need to use one server. Due to the lightweight nature of greenlets a server should be able to spawn tens of thousands of jobs concurrently, which is way more than most applications need.

@aguegu
Copy link

aguegu commented May 29, 2014

ok, so there would be no need for "-w 4"

But socket.io / flask-socketio does not work directly by proxy on nginx (port 80).

Look forwards to your solution / blog on this, which would complete your book also.

@alcinos
Copy link

alcinos commented May 31, 2014

Some news here, I finally got it to work as expected. Not sure what was the problem though, some possible hints would be the version of Nginx (I use 1.6.0), and a line I changed inside the conf file : I added explicit listening to ipv4 addresses. The relevant lines are listed above :

server {
  listen [::]:80;
  listen 80;
  server_name localhost;
…

Hope that helps

@xr09
Copy link

xr09 commented May 31, 2014

@alcinos So, should we create a new wiki page with all this? I know for sure I'll need it in the future.
https://github.com/miguelgrinberg/Flask-SocketIO/wiki/_new

@aguegu
Copy link

aguegu commented May 31, 2014

@miguelgrinberg Here is my demo: a raspberry pi in my house post my room temperature every 10 sec if it diffs from last commits. the curve would append new node if new data uploaded by flask-socketio. it works on port 5000

http://115.29.223.207:5000/sensor/538147497943f70bcf756146

but socketio does not work on port 80 (forwards by nginx):

http://115.29.223.207/sensor/538147497943f70bcf756146

You may check the chrome console, and you would see socket.io runs time out all the time on port 80.
I do not know how to make the nginx conf right.

@alcinos
Copy link

alcinos commented May 31, 2014

@aguegu Maybe you could post your nginx conf so that we try to see what's wrong.
Also, consider using gunicorn to run your app, its completely transparent but much more robust (better exception handling,…).

@miguelgrinberg
Copy link
Owner

I appreciate all the effort you guys are putting in figuring this thing out. I hope I will have some time this weekend to look into this. If I manage to make this work I will update the documentation with the recipe.

@aguegu
Copy link

aguegu commented Jun 1, 2014

my sites-enable/busykoala:

server {
listen 80;
server_name www.busykoala.com
access_log /var/log/nginx/example.log;

location / {
    proxy_pass          http://127.0.0.1:5000;
    proxy_redirect      off;

    proxy_set_header    Host            $host;
    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
}

location /socket.io {
    proxy_pass          http://127.0.0.1:5000/socket.io;
    proxy_redirect      off;

    proxy_set_header    Host             $host;
    proxy_set_header    X-Real-IP        $remote_addr;
    proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;

    # Websockets support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

}

same as @alcinos 's

@miguelgrinberg
Copy link
Owner

I'm happy to report success on this. I have been able to run the example app on gunicorn and/or werkzeug on localhost, while nginx proxying it to the outside world.

First it is important to use a recent nginx release. I built latest version 1.7.1 from source, In theory any release 1.4 and up will work, but releases before 1.4 do not have proxy support for WebSocket.

My configuration very similar to the one shown above:

server {
    listen 80;
    server_name localhost;
    access_log /var/log/nginx/example.log;

    location /socket.io {
        proxy_pass http://127.0.0.1:5000/socket.io;
        proxy_redirect off;
        proxy_buffering off;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

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

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_redirect off;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

If you can't still make it work then please try the latest nginx release. If that still doesn't do it, then check the javascript console and see what error(s) you get.

@aguegu
Copy link

aguegu commented Jun 4, 2014

@miguelgrinberg it works. It turns out that my nginx is too old on my hosting of ubuntu 12.04. It was 1.1.x. As I added ppa: nginx/stable and upgrade nginx to 1.6.0, the problem is solved and socketio works.

@nikitph
Copy link

nikitph commented Apr 30, 2016

can someone elaborate on these lines:

location /socket.io {
proxy_pass http://127.0.0.1:5000/socket.io;

does this mean it points to the route /socket.io in the flask app? essentially how does this map to the flask app? is it transparent? meaning i dont have to make any changes in the actual app?

@miguelgrinberg
Copy link
Owner

@nikitph the /socket.io endpoint is handled by Flask-SocketIO. When you create a SocketIO instance and pass your Flask app to it, the extension inserts its endpoint into the app. You do not need to make any changes to the application, as any requests that are not going to /socket.io are passed through to the application.

@nikitph
Copy link

nikitph commented Apr 30, 2016

@miguelgrinberg thanks a lot miguel. big fan of your work.

interesting. so do i need to supply this on the client? locally everything works very smoothly. i am above version 1.4 for nginx too.

my client looks like this

var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
socket.on('connect', function () {
socket.emit('join', {room: 1});
});

this is my local server setting

manage.py -->
def server():
app.debug = True
# WSGIServer(('127.0.0.1', 5000), app).serve_forever()
eventlet.wsgi.server(eventlet.listen(('127.0.0.1', 5000)), app)

now this is my deploy script on digital ocean

exec gunicorn --timeout=300 -b 127.0.0.1:{{port}} manage:app -w $NUM_WORKERS
--user=$USER --group=$GROUP --log-level=debug
--log-file=$LOGFILE 2>>$LOGFILE --worker-class=eventlet --worker-connections 1001

and this is the nginx config -->

server {
listen 80;
server_name {{server_hostname}} ;
return 301 http://www.{{server_hostname}}$request_uri;
}

server {

listen 80;
server_name {{ ansible_eth0.ipv4.address }} www.{{server_hostname}};

root /home/{{user_name}}/{{server_hostname}};

location  /static {
        alias /home/{{user_name}}/{{server_hostname}}/static;
        autoindex on;
        expires max;
    }

#deny access to git and dot files
location ~ /\.  { 
deny all;
return 404;
}

#deny direct access to script and sensitive files 
location ~* \.(pl|cgi|py|sh|lua|log|md5)$ {
    return 444;
    }

location / {
    proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://127.0.0.1:{{enferno_port}};

}

location /socket.io {
    proxy_pass http://127.0.0.1:{{enferno_port}}/socket.io;
    proxy_redirect off;
    proxy_buffering off;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

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

}

but i get a bunch of JS errors on the client and this on the backend

2016-04-30 17:48:30 [20877] [DEBUG] GET /socket.io/
2016-04-30 17:48:30 [20877] [DEBUG] Closing connection.
2016-04-30 17:48:30 [20879] [DEBUG] POST /socket.io/
2016-04-30 17:48:30 [20879] [DEBUG] Closing connection.
2016-04-30 17:48:30 [20879] [DEBUG] GET /socket.io/
2016-04-30 17:48:30 [20879] [DEBUG] Closing connection.
2016-04-30 17:48:30 [20880] [DEBUG] POST /socket.io/
2016-04-30 17:48:30 [20880] [DEBUG] Closing connection.

appreciate the help.

@nikitph
Copy link

nikitph commented Apr 30, 2016

i just saw i m loading on a closed bug. LMK if you want a new one opened.

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

No branches or pull requests

9 participants