Browse Source

Updates for recent changes in main

pull/1164/head
Miguel Grinberg 2 years ago
parent
commit
ee86b0e8d0
Failed to extract signature
  1. 20
      examples/server/asgi/app.py
  2. 18
      examples/server/wsgi/app.py
  3. 64
      src/socketio/admin.py
  4. 15
      src/socketio/async_server.py
  5. 71
      src/socketio/asyncio_admin.py
  6. 15
      src/socketio/server.py

20
examples/server/asgi/app.py

@ -1,9 +1,25 @@
#!/usr/bin/env python
import uvicorn
# set instrument to `True` to accept connections from the official Socket.IO
# Admin UI hosted at https://admin.socket.io
instrument = True
admin_login = {
'username': 'admin',
'password': 'python', # change this to a strong secret for production use!
}
import uvicorn
import socketio
sio = socketio.AsyncServer(async_mode='asgi')
sio = socketio.AsyncServer(
async_mode='asgi',
cors_allowed_origins=None if not instrument else [
'http://localhost:5000',
'https://admin.socket.io', # edit the allowed origins if necessary
])
if instrument:
sio.instrument(auth=admin_login)
app = socketio.ASGIApp(sio, static_files={
'/': 'app.html',
})

18
examples/server/wsgi/app.py

@ -3,10 +3,26 @@
# installed
async_mode = None
# set instrument to `True` to accept connections from the official Socket.IO
# Admin UI hosted at https://admin.socket.io
instrument = False
admin_login = {
'username': 'admin',
'password': 'python', # change this to a strong secret for production use!
}
from flask import Flask, render_template
import socketio
sio = socketio.Server(logger=True, async_mode=async_mode)
sio = socketio.Server(
async_mode=async_mode,
cors_allowed_origins=None if not instrument else [
'http://localhost:5000',
'https://admin.socket.io', # edit the allowed origins if necessary
])
if instrument:
sio.instrument(auth=admin_login)
app = Flask(__name__)
app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
app.config['SECRET_KEY'] = 'secret!'

64
src/socketio/admin.py

@ -4,6 +4,7 @@ import os
import socket
import time
from urllib.parse import parse_qs
from .exceptions import ConnectionRefusedError
HOSTNAME = socket.gethostname()
PID = os.getpid()
@ -92,8 +93,8 @@ class InstrumentedServer:
self.sio.manager.leave_room = self._leave_room
# report emit events
self.sio.__emit_internal = self.sio._emit_internal
self.sio._emit_internal = self._emit_internal
self.sio.manager.__emit = self.sio.manager.emit
self.sio.manager.emit = self._emit
# report receive events
self.sio.__handle_event_internal = self.sio._handle_event_internal
@ -117,9 +118,16 @@ class InstrumentedServer:
self.__class__._eio_websocket_handler, self)
def admin_connect(self, sid, environ, client_auth):
if self.auth:
if not self.auth(client_auth):
raise ConnectionRefusedError('Invalid credentials')
if self.auth != None:
authenticated = False
if isinstance(self.auth, dict):
authenticated = client_auth == self.auth
elif isinstance(self.auth, list):
authenticated = client_auth in self.auth
else:
authenticated = self.auth(client_auth)
if not authenticated:
raise ConnectionRefusedError('authentication failed')
def config(sid):
self.sio.sleep(0.1)
@ -183,13 +191,16 @@ class InstrumentedServer:
def check_for_upgrade():
for _ in range(5):
self.sio.sleep(5)
if self.sio.eio._get_socket(eio_sid).upgraded:
self.sio.emit('socket_updated', {
'id': sid,
'nsp': namespace,
'transport': 'websocket',
}, namespace=self.admin_namespace)
break
try:
if self.sio.eio._get_socket(eio_sid).upgraded:
self.sio.emit('socket_updated', {
'id': sid,
'nsp': namespace,
'transport': 'websocket',
}, namespace=self.admin_namespace)
break
except KeyError:
pass
if serialized_socket['transport'] == 'polling':
self.sio.start_background_task(check_for_upgrade)
@ -226,17 +237,24 @@ class InstrumentedServer:
), namespace=self.admin_namespace)
return self.sio.manager.__leave_room(sid, namespace, room)
def _emit_internal(self, eio_sid, event, data, namespace=None, id=None):
ret = self.sio.__emit_internal(eio_sid, event, data,
namespace=namespace, id=id)
def _emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
ret = self.sio.manager.__emit(event, data, namespace, room=room,
skip_sid=skip_sid, callback=callback,
**kwargs)
if namespace != self.admin_namespace:
sid = self.sio.manager.sid_from_eio_sid(eio_sid, namespace)
self.sio.emit('event_sent', (
namespace,
sid,
[event] + list(data) if isinstance(data, tuple) else [data],
datetime.utcnow().isoformat() + 'Z',
), namespace=self.admin_namespace)
event_data = [event] + list(data) if isinstance(data, tuple) \
else [data]
if not isinstance(skip_sid, list):
skip_sid = [skip_sid]
for sid, _ in self.sio.manager.get_participants(namespace, room):
if sid not in skip_sid:
self.sio.emit('event_sent', (
namespace,
sid,
event_data,
datetime.utcnow().isoformat() + 'Z',
), namespace=self.admin_namespace)
return ret
def _handle_event_internal(self, server, sid, eio_sid, data, namespace,
@ -282,7 +300,7 @@ class InstrumentedServer:
def _wait(ws):
ret = ws.__wait()
self.event_buffer.push('packetsIn')
self.event_buffer.push('bytesIn', len(ret))
self.event_buffer.push('bytesIn', len(ret or ''))
return ret
ws.__send = ws.send

15
src/socketio/async_server.py

@ -472,11 +472,16 @@ class AsyncServer(base_server.BaseServer):
"""Instrument the Socket.IO server for monitoring with the `Socket.IO
Admin UI <https://socket.io/docs/v4/admin-ui/>`_.
:param auth: A function that receives a dictionary with the credentials
provided by the client (usually ``username`` and
``password``) and returns ``True`` if the user is allowed.
To disable authentication, set this argument to ``False``
(not recommended, never do this on a production server).
:param auth: Authentication credentials for Admin UI access. Set to a
dictionary with the expected login (usually ``username``
and ``password``) or a list of dictionaries if more than
one set of credentials need to be available. For more
complex authentication methods, set to a callable that
receives the authentication dictionary as an argument and
returns ``True`` if the user is allowed or ``False``
otherwise. To disable authentication, set this argument to
``False`` (not recommended, never do this on a production
server).
:param mode: The reporting mode. The default is ``'development'``,
which is best used while debugging, as it may have a
significant performance effect. Set to ``'production'`` to

71
src/socketio/asyncio_admin.py

@ -6,6 +6,7 @@ import socket
import time
from urllib.parse import parse_qs
from .admin import EventBuffer
from .exceptions import ConnectionRefusedError
HOSTNAME = socket.gethostname()
PID = os.getpid()
@ -73,8 +74,8 @@ class InstrumentedAsyncServer:
self.sio.manager.leave_room = self._leave_room
# report emit events
self.sio.__emit_internal = self.sio._emit_internal
self.sio._emit_internal = self._emit_internal
self.sio.manager.__emit = self.sio.manager.emit
self.sio.manager.emit = self._emit
# report receive events
self.sio.__handle_event_internal = self.sio._handle_event_internal
@ -98,13 +99,19 @@ class InstrumentedAsyncServer:
async def admin_connect(self, sid, environ, client_auth):
authenticated = True
if self.auth:
if asyncio.iscoroutinefunction(self.auth):
authenticated = await self.auth(client_auth)
if self.auth != None:
authenticated = False
if isinstance(self.auth, dict):
authenticated = client_auth == self.auth
elif isinstance(self.auth, list):
authenticated = client_auth in self.auth
else:
authenticated = self.auth(client_auth)
if not authenticated:
raise ConnectionRefusedError('Invalid credentials')
if asyncio.iscoroutinefunction(self.auth):
authenticated = await self.auth(client_auth)
else:
authenticated = self.auth(client_auth)
if not authenticated:
raise ConnectionRefusedError('authentication failed')
async def config(sid):
await self.sio.sleep(0.1)
@ -137,7 +144,6 @@ class InstrumentedAsyncServer:
await self.sio.emit(event, data, to=room_filter, namespace=namespace)
def admin_enter_room(self, _, namespace, room, room_filter=None):
print(namespace, room, room_filter)
for sid, _ in self.sio.manager.get_participants(
namespace, room_filter):
self.sio.enter_room(sid, room, namespace=namespace)
@ -169,13 +175,16 @@ class InstrumentedAsyncServer:
async def check_for_upgrade():
for _ in range(5):
await self.sio.sleep(5)
if self.sio.eio._get_socket(eio_sid).upgraded:
await self.sio.emit('socket_updated', {
'id': sid,
'nsp': namespace,
'transport': 'websocket',
}, namespace=self.admin_namespace)
break
try:
if self.sio.eio._get_socket(eio_sid).upgraded:
await self.sio.emit('socket_updated', {
'id': sid,
'nsp': namespace,
'transport': 'websocket',
}, namespace=self.admin_namespace)
break
except KeyError:
pass
if serialized_socket['transport'] == 'polling':
self.sio.start_background_task(check_for_upgrade)
@ -212,18 +221,24 @@ class InstrumentedAsyncServer:
)))
return self.sio.manager.__leave_room(sid, namespace, room)
async def _emit_internal(self, eio_sid, event, data, namespace=None,
id=None):
ret = await self.sio.__emit_internal(eio_sid, event, data,
namespace=namespace, id=id)
async def _emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
ret = await self.sio.manager.__emit(event, data, namespace, room=room,
skip_sid=skip_sid, callback=callback,
**kwargs)
if namespace != self.admin_namespace:
sid = self.sio.manager.sid_from_eio_sid(eio_sid, namespace)
await self.sio.emit('event_sent', (
namespace,
sid,
[event] + list(data) if isinstance(data, tuple) else [data],
datetime.utcnow().isoformat() + 'Z',
), namespace=self.admin_namespace)
event_data = [event] + list(data) if isinstance(data, tuple) \
else [data]
if not isinstance(skip_sid, list):
skip_sid = [skip_sid]
for sid, _ in self.sio.manager.get_participants(namespace, room):
if sid not in skip_sid:
await self.sio.emit('event_sent', (
namespace,
sid,
event_data,
datetime.utcnow().isoformat() + 'Z',
), namespace=self.admin_namespace)
return ret
async def _handle_event_internal(self, server, sid, eio_sid, data,
@ -269,7 +284,7 @@ class InstrumentedAsyncServer:
async def _wait(ws):
ret = await ws.__wait()
self.event_buffer.push('packetsIn')
self.event_buffer.push('bytesIn', len(ret))
self.event_buffer.push('bytesIn', len(ret or ''))
return ret
ws.__send = ws.send

15
src/socketio/server.py

@ -459,11 +459,16 @@ class Server(base_server.BaseServer):
"""Instrument the Socket.IO server for monitoring with the `Socket.IO
Admin UI <https://socket.io/docs/v4/admin-ui/>`_.
:param auth: A function that receives a dictionary with the credentials
provided by the client (usually ``username`` and
``password``) and returns ``True`` if the user is allowed.
To disable authentication, set this argument to ``False``
(not recommended, never do this on a production server).
:param auth: Authentication credentials for Admin UI access. Set to a
dictionary with the expected login (usually ``username``
and ``password``) or a list of dictionaries if more than
one set of credentials need to be available. For more
complex authentication methods, set to a callable that
receives the authentication dictionary as an argument and
returns ``True`` if the user is allowed or ``False``
otherwise. To disable authentication, set this argument to
``False`` (not recommended, never do this on a production
server).
:param mode: The reporting mode. The default is ``'development'``,
which is best used while debugging, as it may have a
significant performance effect. Set to ``'production'`` to

Loading…
Cancel
Save