Flask-socketio: Question: How to customize url from /socket.io to something else?

Created on 5 Aug 2016  路  32Comments  路  Source: miguelgrinberg/Flask-SocketIO

I was wondering how I can change the url that the websockets use from /socket.io to something else? I tried setting the path parameter when declaring the socket:

socketio = SocketIO(app, path='test_socket.io')

but the websockets still seem to be communicating over /socket.io. Is there something else I need to change in addition to the path parameter to get this to work?

Thanks!

question

Most helpful comment

@MelanieFreed Did you change the client side accordingly? The /socket.io endpoint is a default that is usually implemented in both client and server. If you change the path on the server side, then you need to tell your client what the new path is.

If you are using the JavaScript Socket.IO client, you just need to add a path option on your connection call:

var socket = io.connect('localhost:3000', {
  'path': '/path/to/socket.io';
});

Also note that the Socket.IO path must start with a /. On the server I add the slash if it is missing, so your code above will work just fine, but I'm not sure if all clients will tolerate a path that does not start with the slash.

All 32 comments

@MelanieFreed Did you change the client side accordingly? The /socket.io endpoint is a default that is usually implemented in both client and server. If you change the path on the server side, then you need to tell your client what the new path is.

If you are using the JavaScript Socket.IO client, you just need to add a path option on your connection call:

var socket = io.connect('localhost:3000', {
  'path': '/path/to/socket.io';
});

Also note that the Socket.IO path must start with a /. On the server I add the slash if it is missing, so your code above will work just fine, but I'm not sure if all clients will tolerate a path that does not start with the slash.

Hi @miguelgrinberg, thanks for your response! Oh, I didn't think about that. I've now changed my socket declaration on the server to include the leading /:

socketio = SocketIO(app, path='/test_socket.io')

And I also added this to the client side:

var socket = io.connect('localhost:5000', {'path': '/test_socket.io'});

I see now that the client is trying a GET request to http://localhost:5000/test_socket.io/... But it returns a 404 (NOT FOUND). It seems like the server then is not communicating using /test_socket.io? Maybe I'm still missing something on the server side?

That's odd. I just tested it here and everything seems to work. Below is the patch with my changes, applied directly on top of the latest version of this repository:

diff --git a/example/app.py b/example/app.py
index 7c1f24b..a546296 100755
--- a/example/app.py
+++ b/example/app.py
@@ -10,7 +10,7 @@ async_mode = None

 app = Flask(__name__)
 app.config['SECRET_KEY'] = 'secret!'
-socketio = SocketIO(app, async_mode=async_mode)
+socketio = SocketIO(app, path='/test_socket.io', async_mode=async_mode)
 thread = None


diff --git a/example/templates/index.html b/example/templates/index.html
index 577041e..db454fa 100644
--- a/example/templates/index.html
+++ b/example/templates/index.html
@@ -16,7 +16,8 @@
             // Connect to the Socket.IO server.
             // The connection URL has the following format:
             //     http[s]://<domain>:<port>[/<namespace>]
-            var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
+            var socket = io.connect('http://' + document.domain + ':' + location.port + namespace,
+                {'path': '/test_socket.io'});

             // Event handler for new connections.
             // The callback function is invoked when a connection with the

That's interesting, thanks so much for testing it out. Strangely, I finally got it working by setting the "resource" parameter on the server instead of the "path" parameter. Not sure why that would matter, especially after looking at the source code...

Before, when I set the "path" parameter, I could successfully curl the websockets at their original location (http://localhost:5000/socket.io/), so it seems like they were stuck there...

I'll have to look back into it to solve this mystery, but for now I'm very happy that it's working. Thanks again for your help! :)

@MelanieFreed what version of Flask-SocketIO do you have? The path argument is relatively new, it used to be resource. The current version accepts both names for this argument.

Oh, that must be it. I'm working with version 2.5. I see it's now at 2.6.1... Mystery solved :)

Hi There,
I have an issue. I am using flask-socketio. I am running flask through wsgi. And url is like https://domain.com/api. but there is no route for /api. I have changed the path from front end socket connection like socket = io('https://domain.com/api/progress-socket', { path: '/api/socket.io' }); but I am not getting any event on front end.
Please let me know what I doing wrong here? Is it because of wsgi?

@susheelbanyal did you set the path also on the SocketIO object?

Yes
socketio = SocketIO(app) if __name__ == '__main__': socketio.run(app, path='/api/socket.io')

Also I did socketio = SocketIO(app, path='/api/socket.io' ) now its giving 404

Exception ignored in: <module 'threading' from '/usr/lib/python3.4/threading.py'>
[Wed Feb 28 07:06:46.945703 2018] [:error] [pid 14744] Traceback (most recent call last):
[Wed Feb 28 07:06:46.945738 2018] [:error] [pid 14744]   File "/usr/lib/python3.4/threading.py", line 1288, in _shutdown
[Wed Feb 28 07:06:46.946201 2018] [:error] [pid 14744]     assert tlock is not None
[Wed Feb 28 07:06:46.946225 2018] [:error] [pid 14744] AssertionError: 

whenever socket tries the url it gives this error in backend flask

Sorry, can't diagnose the problem with the information you provided. Make a small example application that reproduces your problem and I'll debug it here to figure out what's wrong.

hey guys,
having a very similar issue, but I am running flask via Unix with gunicorn,
path is specified for all sides, and what I get for the client side is 502 Bad Gateway, when trying to access http://localhost:port/node/socket.io...

clientside:
socketio = SocketIO(ping_interval=5, ping_timeout=120, path='/node/socket.io', async_mode="gevent")

serverside:
socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/chat', {path: '/node/socket.io'});

nginx configuration:

`

    location /socket.io { 
    include proxy_params;  
    proxy_http_version 1.1;  
    proxy_buffering off;  
    proxy_set_header Upgrade $http_upgrade;  
    proxy_set_header Connection "Upgrade";  
    proxy_pass http://unix:/home/user/chat.sock/socket.io;
}`

am I supposed to 'get' sockets when entering 'http://localhost:port/node/socket.io`?
also I am not sure if it's possible to get socket.io via Unix sock...

shouldn't the location be /node/socket.io in your nginx configuration?

I've changed all paths and location to /socket.io, and now I am able to get sockets by visiting http://localhost:8001/socket.io,
so I am running my app on nginx with gunicorn, and able to get sockets, but clients are not getting authorized, werkzeug gives exception 401, though all sockets' paths are identical to each other...

Werkzeug has no logic to deny clients with a 401 error. These 401s must be coming from your application or from your nginx config.

Is there functionality to support this with string templates or is that something that will be coming soon?
I.E. Using this syntax "/<string:variable_path>/socket".
If not, is there a reason why this feature hasn't been or can't be implemented?

Thanks

@jwitheford what is the purpose of this? You will not be invoking this endpoint more than once per client, I don't see a reason to make it variable. Note that this isn't a Flask route, nor it is a route that your application will handle directly.

The application that I am working on supports multi-tenancy and not every user will belong to the same tenant.
We are relying on the path to handle the internal routing of the application and map each socket connection to a private room that has the same name as whatever path they are on. For example, company1 will have a different path (https://mydomain.com/company1) to company2 (https://mydomain.com/company2) and hence
their web-socket connections need to be in different rooms so that they don't receive each others notifications.
We are also trying to use this path to authenticate users and ensure that they are allowed access to the web-socket service. Our cookies have been issued for domain/path and if we don't allow the path in the socketio route then the browser will not be able to pass the cookies on the handshake.
It would be good to keep the URL naming consistent with that of the other endpoints that are being used as well, so that https://mydomain.com/company1/socketio shows the same information as https://mydomain.com/company1/login and allows everything to work smoothly.

I hope this makes sense.
Cheers

@jwitheford Okay, I think I understand, but you still need to consider what I said above. You do not have any control on handling the Socket.IO URL, these don't even pass through Flask. What you can do is write a WSGI middleware that is similar to the one included with this package, but you can make it do a more elaborate routing. You can make the first path component a wildcard, and that way to can handle Socket.IO on a different path for each company.

I also want to clarify something regarding rooms. Each Socket.IO connection supports multiple rooms. You do not need to connect on a different path to create subsets of clients that can be notified as a group.

Yeah okay then, that makes sense. We might have a go at writing that WSGI middleware then to see if we can get it working, but just thought that it could be a good feature for you to expose as it may be something that other people could make use of too. Are you able to give some pointers as to where the changes would need to be made?

Thanks for your help

@jwitheford look at this class: https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/flask_socketio/__init__.py#L29 And also its parent class: https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/middleware.py.

The routing logic is here: https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/middleware.py#L45-L49. Your middleware will have to keep the style of mine, but change the routing to support those wildcards in the path. Should not be very complicated to write.

io.connect({
  'path': window.location.pathname + 'socket.io'
});
io.connect({
  'path': window.location.pathname + 'socket.io'
});

God bless you

i want to custom connection and change it from https://site.com to http://site.com
because i have a site with ssl and when i connect like that io.connect('http://site.com') changed in console to https://site.com and that make a problem with me

so you want to connect to socket.io over http while your client app is served over https? Browsers may object to this by flagging it as a mixed content request and blocking it without user intervention. Chrome only provides an icon on the right side of the omnibar when this happens.

Greeting,
I'm still having a problem similar to this. I'm using Flask-SocketIO==4.3.0 on the backend and socket.io-client on the frontend.
I have a local setup that works fine in which every time I send a GET request to a /broadcast_event endpoint on my server, it then broadcasts an event, making a small toast message pop up all my open frontends.

My frontend code:

import React from 'react';
import openSocket from 'socket.io-client';
import { toast } from 'react-toastify';

const socket = openSocket('http://localhost:5000');

const ToastContent = ({ text, icon }) => (
  <div>
    {text} {icon}
  </div>
);

export class SocketComponent extends React.Component {
  constructor(props) {
    super(props);
    socket.on('greeting', greeting => {
      console.log(`this is my BE message:${JSON.stringify(greeting)}`);
      toast.info(<ToastContent text={`I received a socket event from the backend! Message: ${greeting.greeting}`} />, {
        autoClose: true,
      });
    });
  }

  render() {
    return <div />;
  }
}

export default SocketComponent;

My python code:

app.py:

socketio = SocketIO(app, cors_allowed_origins="*")

if __name__ == '__main__':
    socketio.run(app, debug=True)

endpoints.py:

from backend.app import app, socketio
@app.route('/api/fruits/broadcast_event', methods=['GET'])
def health_check_slb():
    logger.info('Im here. BROADCASTING EVENT OVER SOCKETS')
    socketio.emit('greeting', {'greeting': 'hello'}, broadcast=True)
    return '', 200

This works fine. But I did not want my frontend to use http://localhost:5000/socket.io/ (which keeps polling all the time). So I switched on both sides to

Frontend:

const socket = openSocket('http://localhost:5000',  {
  path: '/api/fruits',
});

and
Backend:

socketio = SocketIO(app, cors_allowed_origins="*", path='/api/fruits')

And then it just stopped working.
I also tried

socketio = SocketIO(app, cors_allowed_origins="*", path='/api/fruits', resource='/api/fruits')

to no avail.

What is see is the frontend correctly using
http://localhost:5000/api/fruits/?EIO=3&transport=polling&t=N89C2LB and getting 200 OK
But on the backend side I keep seeing
app : GET - /socket.io/?EIO=3&transport=polling&t=1589292139484-3072 - 404

And the broadcast_event endpoint is no longer able to successfully broadcast the event to the frontend.

What am I missing?

@aqueiros if the frontend is sending your Socket.IO requests on /api/fruits and it gets 200 responses, then you surely can see this URL in your server logs as well, correct?

You must be seeing the /socket.io URL in addition to the /api/fruits, yes? Then the answer is simple, you have some old client that hasn't been updated with the new path that is still trying to connect to your server.

Hi @miguelgrinberg. Thank you for your speedy response. Actually, I found out that for some reason, if I add something after /api/fruits on both sides, then it starts working.

Frontend:

const socket = openSocket('http://localhost:5000',  {
  path: '/api/fruits/something',
});

Backend:

socketio = SocketIO(app, cors_allowed_origins="*", path='/api/fruits/something')

Hi @miguelgrinberg , how are you?

I'm having problem to using flask-socketio in subdirectory too, I have read all comments of this issue, but could'n make work in my enviromment

Frontend

if (window.location.protocol == "https:"){ var ws_scheme = "wss://"; }else{ var ws_scheme = "ws://"; } var socket = io.connect(ws_scheme + location.host, {'path':'/webinar'});

Backend

if __name__ == "__main__": socket.socketio.run(app, path='/webinar')

Nginx

`location /webinar {
proxy_pass http://127.0.0.1:8544/webinar;
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;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120;
proxy_connect_timeout 30s;
client_max_body_size 5M;
}

location /socket.io {
    #include proxy_params;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_pass http://127.0.0.1:8544/webinar;

}`

On browser console i get this error

https://<url>/webinar/?EIO=3&transport=polling&t=N8X0Pqu -> Erro 405

On Nginx I have:

"GET /webinar/socket.io/?EIO=3&transport=polling&t=N8X1dz2 HTTP/1.1" 404 209 "https:/<url>/webinar/schedule"

Sorry about the question and the english, I'm not native speaker, I'm from Brazil.

@jrdiniz pass the path argument to the SocketIO() constructor, not in the run() call.

Help me!!!
My problem is the main page @app.route('/') is reflecting the emit event value in html page, but then in that page there is a link which routes to the @app.route('/Tambola/user', methods=['GET']), and this also returns the same html. So on getting the second routed page the value is not showing up there,.. what should I do so that on changing the url also the variable value should show in the Html..

Server_Code
outertambolano=0

thread = Thread()
app = Flask(__name__)
socketio = SocketIO(app)

def func1():
@app.route('/')
def index():
return render_template('index.html')

@socketio.on('connect', namespace='/test')
 def test_connect():
    # need visibility of the global thread object
    global thread
    print('Client connected')

@app.route('/Tambola/user', methods=['GET'])
def api_id():
     return render_template('index.html')

app.run()

while True:
outertambolano = random.randrange(1, 91)
socketio.emit('newnumber', {'number': outertambolano},namespace='/test')
time.sleep(5)

Client_Code
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test',{path:'/Tambola/user'});
var numbers_received = 0;

//receive details from server
//receive details from server
socket.on('newnumber', function(msg) {
    console.log("Received number" + msg.number);
    numbers_received=msg.number;
    numbers_string = '';
        numbers_string = numbers_string + '<h1>' + numbers_received.toString() + '</h1>';
    $('#sockettambola').html(numbers_string);
});

....

@VIjayGupta6572 not sure what you are trying to do, but you cannot use the same path for the Socket.IO endpoint and for a route in your Flask application. Leave the Socket.IO path as default unless you understand how the Socket.IO protocol works.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

novice79 picture novice79  路  3Comments

lnunno picture lnunno  路  4Comments

chaitanyavolkaji picture chaitanyavolkaji  路  3Comments

dlernz picture dlernz  路  4Comments

EndenDragon picture EndenDragon  路  3Comments