Moved client operations to ClientRegistry, added rudimentary chat.
This commit is contained in:
parent
d3e40fb76c
commit
e8b9976208
|
|
@ -1,4 +1,5 @@
|
|||
from owrx.config import Config
|
||||
from datetime import datetime, timedelta
|
||||
import threading
|
||||
|
||||
import logging
|
||||
|
|
@ -10,6 +11,10 @@ class TooManyClientsException(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class BannedClientException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ClientRegistry(object):
|
||||
sharedInstance = None
|
||||
creationLock = threading.Lock()
|
||||
|
|
@ -23,6 +28,7 @@ class ClientRegistry(object):
|
|||
|
||||
def __init__(self):
|
||||
self.clients = []
|
||||
self.bans = {}
|
||||
Config.get().wireProperty("max_clients", self._checkClientCount)
|
||||
super().__init__()
|
||||
|
||||
|
|
@ -33,7 +39,9 @@ class ClientRegistry(object):
|
|||
|
||||
def addClient(self, client):
|
||||
pm = Config.get()
|
||||
if len(self.clients) >= pm["max_clients"]:
|
||||
if self.isIpBanned(client.conn.getIp()):
|
||||
raise BannedClientException()
|
||||
elif len(self.clients) >= pm["max_clients"]:
|
||||
raise TooManyClientsException()
|
||||
self.clients.append(client)
|
||||
self.broadcast()
|
||||
|
|
@ -52,3 +60,58 @@ class ClientRegistry(object):
|
|||
for client in self.clients[new_count:]:
|
||||
logger.debug("closing one connection...")
|
||||
client.close()
|
||||
|
||||
# Broadcast chat message to all connected clients.
|
||||
def broadcastChatMessage(self, sender: str, text: str):
|
||||
for c in self.clients:
|
||||
c.write_chat_message(sender, text)
|
||||
|
||||
# List all active and banned clients.
|
||||
def listAll(self):
|
||||
result = []
|
||||
for c in self.clients:
|
||||
result.append({
|
||||
"ts" : c.conn.getStartTime(),
|
||||
"ip" : c.conn.getIp(),
|
||||
"sdr" : c.sdr.getName(),
|
||||
"band" : c.sdr.getProfileName(),
|
||||
"ban" : False
|
||||
})
|
||||
self.expireBans()
|
||||
for ip in self.bans:
|
||||
result.append({
|
||||
"ts" : self.bans[ip],
|
||||
"ip" : ip,
|
||||
"ban" : True
|
||||
})
|
||||
return result
|
||||
|
||||
# Ban a client, by IP, for given number of minutes.
|
||||
def banIp(self, ip: str, minutes: int):
|
||||
self.expireBans()
|
||||
self.bans[ip] = datetime.now() + timedelta(minutes=minutes)
|
||||
banned = []
|
||||
for c in self.clients:
|
||||
if ip == c.conn.getIp():
|
||||
banned.append(c)
|
||||
for c in banned:
|
||||
try:
|
||||
c.close()
|
||||
except:
|
||||
logger.exception("exception while banning %s" % ip)
|
||||
|
||||
# Unban a client, by IP.
|
||||
def unbanIp(self, ip: str):
|
||||
if ip in self.bans:
|
||||
del self.bans[ip]
|
||||
|
||||
# Check if given IP is banned at the moment.
|
||||
def isIpBanned(self, ip: str):
|
||||
return ip in self.bans and datetime.now() < self.bans[ip]
|
||||
|
||||
# Delete all expired bans.
|
||||
def expireBans(self):
|
||||
now = datetime.now()
|
||||
old = [ip for ip in self.bans if now >= self.bans[ip]]
|
||||
for ip in old:
|
||||
del self.bans[ip]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from owrx.dsp import DspManager
|
|||
from owrx.cpu import CpuUsageThread
|
||||
from owrx.sdr import SdrService
|
||||
from owrx.source import SdrSourceState, SdrClientClass, SdrSourceEventClient
|
||||
from owrx.client import ClientRegistry, TooManyClientsException
|
||||
from owrx.client import ClientRegistry, TooManyClientsException, BannedClientException
|
||||
from owrx.feature import FeatureDetector
|
||||
from owrx.version import openwebrx_version
|
||||
from owrx.bands import Bandplan
|
||||
|
|
@ -166,6 +166,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
|
|||
self.write_backoff_message("Too many clients")
|
||||
self.close()
|
||||
raise
|
||||
except BannedClientException:
|
||||
self.write_backoff_message("Client IP banned")
|
||||
self.close()
|
||||
raise
|
||||
|
||||
self.setupGlobalConfig()
|
||||
self.stack = self.setupStack()
|
||||
|
|
@ -480,6 +484,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
|
|||
def write_backoff_message(self, reason):
|
||||
self.send({"type": "backoff", "reason": reason})
|
||||
|
||||
def write_chat_message(self, sender, text):
|
||||
logger.debug("Sending {0}".format({"type": "chat_message", "sender": sender, "text": text}))
|
||||
self.send({"type": "chat_message", "sender": sender, "text": text})
|
||||
|
||||
def write_modes(self, modes):
|
||||
def to_json(m):
|
||||
res = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from owrx.controllers.admin import AuthorizationMixin
|
||||
from owrx.controllers.template import WebpageController
|
||||
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem, BreadcrumbMixin
|
||||
from owrx.websocket import WebSocketConnection
|
||||
from owrx.client import ClientRegistry
|
||||
import json
|
||||
import re
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ class ClientController(AuthorizationMixin, WebpageController):
|
|||
</tr>
|
||||
</table>
|
||||
""".format(
|
||||
clients="".join(ClientController.renderClient(c) for c in WebSocketConnection.listAll())
|
||||
clients="".join(ClientController.renderClient(c) for c in ClientRegistry.getSharedInstance().listAll())
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -81,9 +81,10 @@ class ClientController(AuthorizationMixin, WebpageController):
|
|||
mins = int(data["mins"]) if "mins" in data else 0
|
||||
if "ip" in data and mins > 0:
|
||||
logger.info("Banning {0} for {1} minutes".format(data["ip"], mins))
|
||||
WebSocketConnection.banIp(data["ip"], mins)
|
||||
ClientRegistry.getSharedInstance().banIp(data["ip"], mins)
|
||||
self.send_response("{}", content_type="application/json", code=200)
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.debug("ban(): " + str(e))
|
||||
self.send_response("{}", content_type="application/json", code=400)
|
||||
|
||||
def unban(self):
|
||||
|
|
@ -91,7 +92,8 @@ class ClientController(AuthorizationMixin, WebpageController):
|
|||
data = json.loads(self.get_body().decode("utf-8"))
|
||||
if "ip" in data:
|
||||
logger.info("Unbanning {0}".format(data["ip"]))
|
||||
WebSocketConnection.unbanIp(data["ip"])
|
||||
ClientRegistry.getSharedInstance().unbanIp(data["ip"])
|
||||
self.send_response("{}", content_type="application/json", code=200)
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.debug("unban(): " + str(e))
|
||||
self.send_response("{}", content_type="application/json", code=400)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ from owrx.controllers.session import SessionController
|
|||
from owrx.controllers.profile import ProfileController
|
||||
from owrx.controllers.imageupload import ImageUploadController
|
||||
from owrx.controllers.robots import RobotsController
|
||||
from owrx.websocket import WebSocketConnection
|
||||
from owrx.controllers.chat import ChatController
|
||||
from owrx.client import ClientRegistry
|
||||
from owrx.storage import Storage
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
|
@ -171,6 +172,7 @@ class Router(object):
|
|||
StaticRoute("/pwchange", ProfileController, method="POST", options={"action": "processPwChange"}),
|
||||
StaticRoute("/imageupload", ImageUploadController),
|
||||
StaticRoute("/imageupload", ImageUploadController, method="POST", options={"action": "processImage"}),
|
||||
StaticRoute("/msgsend", ChatController, method="POST", options={"action": "send"}),
|
||||
]
|
||||
|
||||
def find_route(self, request):
|
||||
|
|
@ -179,7 +181,7 @@ class Router(object):
|
|||
return r
|
||||
|
||||
def route(self, handler, request):
|
||||
if WebSocketConnection.isIpBanned(handler.client_address[0]):
|
||||
if ClientRegistry.getSharedInstance().isIpBanned(handler.client_address[0]):
|
||||
handler.send_error(404, "Not Found", "The page you requested could not be found.")
|
||||
else:
|
||||
route = self.find_route(request)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from multiprocessing import Pipe
|
|||
import select
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
|
||||
import logging
|
||||
|
||||
|
|
@ -47,7 +47,6 @@ class Handler(ABC):
|
|||
|
||||
class WebSocketConnection(object):
|
||||
connections = []
|
||||
bans = {}
|
||||
|
||||
@staticmethod
|
||||
def closeAll():
|
||||
|
|
@ -57,59 +56,6 @@ class WebSocketConnection(object):
|
|||
except:
|
||||
logger.exception("exception while shutting down websocket connections")
|
||||
|
||||
@staticmethod
|
||||
def listAll():
|
||||
result = []
|
||||
for c in WebSocketConnection.connections:
|
||||
entry = {
|
||||
"ts" : c.startTime,
|
||||
"ip" : c.handler.client_address[0],
|
||||
"ban" : False
|
||||
}
|
||||
rx = c.messageHandler
|
||||
if hasattr(rx, "sdr"):
|
||||
entry["sdr"] = rx.sdr.getName()
|
||||
entry["band"] = rx.sdr.getProfileName()
|
||||
result.append(entry)
|
||||
WebSocketConnection.cleanBans()
|
||||
for ip in WebSocketConnection.bans:
|
||||
result.append({
|
||||
"ts" : WebSocketConnection.bans[ip],
|
||||
"ip" : ip,
|
||||
"ban" : True
|
||||
})
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def banIp(ip: str, minutes: int):
|
||||
WebSocketConnection.cleanBans()
|
||||
WebSocketConnection.bans[ip] = datetime.now() + timedelta(minutes=minutes)
|
||||
banned = []
|
||||
for c in WebSocketConnection.connections:
|
||||
if ip == c.handler.client_address[0]:
|
||||
banned.append(c)
|
||||
for c in banned:
|
||||
try:
|
||||
c.close()
|
||||
except:
|
||||
logger.exception("exception while banning %s" % ip)
|
||||
|
||||
@staticmethod
|
||||
def unbanIp(ip: str):
|
||||
if ip in WebSocketConnection.bans:
|
||||
del WebSocketConnection.bans[ip]
|
||||
|
||||
@staticmethod
|
||||
def isIpBanned(ip: str):
|
||||
return ip in WebSocketConnection.bans and datetime.now() < WebSocketConnection.bans[ip]
|
||||
|
||||
@staticmethod
|
||||
def cleanBans():
|
||||
now = datetime.now()
|
||||
old = [ip for ip in WebSocketConnection.bans if now >= WebSocketConnection.bans[ip]]
|
||||
for ip in old:
|
||||
del WebSocketConnection.bans[ip]
|
||||
|
||||
def __init__(self, handler, messageHandler: Handler):
|
||||
self.startTime = datetime.now()
|
||||
self.handler = handler
|
||||
|
|
@ -352,3 +298,9 @@ class WebSocketConnection(object):
|
|||
def sendPong(self):
|
||||
header = self.get_header(0, OPCODE_PONG)
|
||||
self._sendBytes(header)
|
||||
|
||||
def getIp(self):
|
||||
return self.handler.client_address[0]
|
||||
|
||||
def getStartTime(self):
|
||||
return self.startTime
|
||||
|
|
|
|||
Loading…
Reference in New Issue