简体   繁体   中英

Python socket.io server error 400 (NodeJS server works)

I'm trying to make JavaScript client to a Python websocket server through an Apache2 proxy.

The client is dead simple:

        const socket = io({
          transports: ['websocket']
        });

I have a NodeJS websocket server and a working Apache2 reverse proxy setup.

Now I want to replace the NodeJS server with a Python server - but none of the example implementations from socket.io works. With each of the my client reports an "error 400" when setting up the websocket connection.

The Python server examples come from here: https://github.com/miguelgrinberg/python-socketio/tree/master/examples/server

Error 400 stands for "Bad Request" - but I know that my requests are fine because my NodeJS server understands them.

When not running behind a proxy then all Python examples work fine.

What could be the problem?

I found the solution - all the Python socket.io server examples that I refered to are not configured to run behind a reverse proxy. The reason is, that the socket.io server is managing a list of allowed request origins and the automatic list creation is failing in the reverse proxy situation.

This function creates the automatic list of allowed origins (engineio/asyncio_server.py):

    def _cors_allowed_origins(self, environ):
        default_origins = []
        if 'wsgi.url_scheme' in environ and 'HTTP_HOST' in environ:
            default_origins.append('{scheme}://{host}'.format(
                scheme=environ['wsgi.url_scheme'], host=environ['HTTP_HOST']))
            if 'HTTP_X_FORWARDED_HOST' in environ:
                scheme = environ.get(
                    'HTTP_X_FORWARDED_PROTO',
                    environ['wsgi.url_scheme']).split(',')[0].strip()
                default_origins.append('{scheme}://{host}'.format(
                    scheme=scheme, host=environ['HTTP_X_FORWARDED_HOST'].split(
                        ',')[0].strip()))

As you can see, it only adds URLs with {scheme} as a protocol. When behind a reverse proxy, {scheme} will always be "http". So if the initial request was HTTPS based, it will not be in the list of allowed origins.

The solution to this problem is very simple: when creating the socket.io server, you have to either tell him to allow all origins or specify your origin:

import socketio
sio = socketio.AsyncServer(cors_allowed_origins="*") # allow all
# or
sio = socketio.AsyncServer(cors_allowed_origins="https://example.com") # allow specific

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM