Начальная реализация транспорта ws для max web и прочие улучшения

This commit is contained in:
Alexey Polyakov
2026-04-07 12:36:30 +03:00
parent 52949602af
commit 0ffc649dd9
19 changed files with 873 additions and 228 deletions

View File

@@ -1,12 +1,18 @@
import asyncio, logging, traceback
import asyncio
import logging
import traceback
from common.opcodes import Opcodes
from common.proto_tcp import MobileProto
from oneme.processors import Processors
from common.rate_limiter import RateLimiter
from common.tools import Tools
from common.opcodes import Opcodes
from oneme.processors import Processors
class OnemeMobileServer:
def __init__(self, host, port, ssl_context, db_pool, clients, send_event, telegram_bot):
class OnemeMobile:
def __init__(
self, host, port, ssl_context, db_pool, clients, send_event, telegram_bot
):
self.host = host
self.port = port
self.ssl_context = ssl_context
@@ -17,14 +23,19 @@ class OnemeMobileServer:
self.proto = MobileProto()
self.auth_required = Tools().auth_required
self.processors = Processors(db_pool=db_pool, clients=clients, send_event=send_event, telegram_bot=telegram_bot)
self.processors = Processors(
db_pool=db_pool,
clients=clients,
send_event=send_event,
telegram_bot=telegram_bot,
)
self.opcodes = Opcodes()
# rate limiter anti ddos brute force protection
self.auth_rate_limiter = RateLimiter(max_attempts=5, window_seconds=60)
self.read_timeout = 300 # Таймаут чтения из сокета (секунды)
self.max_read_size = 65536 # Максимальный размер данных из сокета
self.read_timeout = 300 # Таймаут чтения из сокета (секунды)
self.max_read_size = 65536 # Максимальный размер данных из сокета
async def handle_client(self, reader, writer):
"""Функция для обработки подключений"""
@@ -44,28 +55,32 @@ class OnemeMobileServer:
# Читаем новые данные из сокета с таймаутом
try:
data = await asyncio.wait_for(
reader.read(self.max_read_size),
timeout=self.read_timeout
reader.read(self.max_read_size), timeout=self.read_timeout
)
except asyncio.TimeoutError:
self.logger.info(f"Таймаут соединения для {address[0]}:{address[1]}")
self.logger.info(
f"Таймаут соединения для {address[0]}:{address[1]}"
)
break
# Если сокет закрыт - выходим из цикла
if not data:
break
if len(data) > self.max_read_size:
self.logger.warning(f"Пакет от {address[0]}:{address[1]} превышает лимит ({len(data)} байт)")
self.logger.warning(
f"Пакет от {address[0]}:{address[1]} превышает лимит ({len(data)} байт)"
)
break
# Распаковываем данные
packet = self.proto.unpack_packet(data)
# Скип если пакет невалидный
# Скип если пакет невалидный
if packet is None:
self.logger.warning(f"Невалидный пакет от {address[0]}:{address[1]}")
self.logger.warning(
f"Невалидный пакет от {address[0]}:{address[1]}"
)
continue
opcode = packet.get("opcode")
@@ -74,34 +89,70 @@ class OnemeMobileServer:
match opcode:
case self.opcodes.SESSION_INIT:
deviceType, deviceName = await self.processors.session_init(payload, seq, writer)
deviceType, deviceName = await self.processors.session_init(
payload, seq, writer
)
case self.opcodes.AUTH_REQUEST:
if not self.auth_rate_limiter.is_allowed(address[0]):
await self.processors._send_error(seq, self.opcodes.AUTH_REQUEST, self.processors.error_types.RATE_LIMITED, writer)
await self.processors._send_error(
seq,
self.opcodes.AUTH_REQUEST,
self.processors.error_types.RATE_LIMITED,
writer,
)
else:
await self.processors.auth_request(payload, seq, writer)
case self.opcodes.AUTH:
if not self.auth_rate_limiter.is_allowed(address[0]):
await self.processors._send_error(seq, self.opcodes.AUTH, self.processors.error_types.RATE_LIMITED, writer)
await self.processors._send_error(
seq,
self.opcodes.AUTH,
self.processors.error_types.RATE_LIMITED,
writer,
)
else:
await self.processors.auth(payload, seq, writer, deviceType, deviceName)
await self.processors.auth(
payload, seq, writer, deviceType, deviceName
)
case self.opcodes.AUTH_CONFIRM:
if not self.auth_rate_limiter.is_allowed(address[0]):
await self.processors._send_error(seq, self.opcodes.AUTH_CONFIRM, self.processors.error_types.RATE_LIMITED, writer)
await self.processors._send_error(
seq,
self.opcodes.AUTH_CONFIRM,
self.processors.error_types.RATE_LIMITED,
writer,
)
elif payload and payload.get("tokenType") == "REGISTER":
await self.processors.auth_confirm(payload, seq, writer, deviceType, deviceName)
await self.processors.auth_confirm(
payload, seq, writer, deviceType, deviceName
)
else:
self.logger.warning(f"AUTH_CONFIRM с неизвестным tokenType: {payload}")
self.logger.warning(
f"AUTH_CONFIRM с неизвестным tokenType: {payload}"
)
case self.opcodes.LOGIN:
if not self.auth_rate_limiter.is_allowed(address[0]):
await self.processors._send_error(seq, self.opcodes.LOGIN, self.processors.error_types.RATE_LIMITED, writer)
await self.processors._send_error(
seq,
self.opcodes.LOGIN,
self.processors.error_types.RATE_LIMITED,
writer,
)
else:
userPhone, userId, hashedToken = await self.processors.login(payload, seq, writer)
(
userPhone,
userId,
hashedToken,
) = await self.processors.login(payload, seq, writer)
if userPhone:
await self._finish_auth(writer, address, userPhone, userId)
await self._finish_auth(
writer, address, userPhone, userId
)
case self.opcodes.LOGOUT:
await self.processors.logout(seq, writer, hashedToken=hashedToken)
await self.processors.logout(
seq, writer, hashedToken=hashedToken
)
break
case self.opcodes.PING:
await self.processors.ping(payload, seq, writer)
@@ -109,35 +160,75 @@ class OnemeMobileServer:
await self.processors.log(payload, seq, writer)
case self.opcodes.ASSETS_UPDATE:
await self.auth_required(
userPhone, self.processors.assets_update, payload, seq, writer
userPhone,
self.processors.assets_update,
payload,
seq,
writer,
)
case self.opcodes.VIDEO_CHAT_HISTORY:
await self.auth_required(
userPhone, self.processors.video_chat_history, payload, seq, writer
userPhone,
self.processors.video_chat_history,
payload,
seq,
writer,
)
case self.opcodes.MSG_SEND:
await self.auth_required(
userPhone, self.processors.msg_send, payload, seq, writer, userId, self.db_pool
userPhone,
self.processors.msg_send,
payload,
seq,
writer,
userId,
self.db_pool,
)
case self.opcodes.FOLDERS_GET:
await self.auth_required(
userPhone, self.processors.folders_get, payload, seq, writer, userPhone
userPhone,
self.processors.folders_get,
payload,
seq,
writer,
userPhone,
)
case self.opcodes.SESSIONS_INFO:
await self.auth_required(
userPhone, self.processors.sessions_info, payload, seq, writer, userPhone, hashedToken
userPhone,
self.processors.sessions_info,
payload,
seq,
writer,
userPhone,
hashedToken,
)
case self.opcodes.CHAT_INFO:
await self.auth_required(
userPhone, self.processors.chat_info, payload, seq, writer, userId
userPhone,
self.processors.chat_info,
payload,
seq,
writer,
userId,
)
case self.opcodes.CHAT_HISTORY:
await self.auth_required(
userPhone, self.processors.chat_history, payload, seq, writer, userId
userPhone,
self.processors.chat_history,
payload,
seq,
writer,
userId,
)
case self.opcodes.CONTACT_INFO_BY_PHONE:
await self.auth_required(
userPhone, self.processors.contact_info_by_phone, payload, seq, writer, userId
userPhone,
self.processors.contact_info_by_phone,
payload,
seq,
writer,
userId,
)
case self.opcodes.OK_TOKEN:
await self.auth_required(
@@ -145,15 +236,28 @@ class OnemeMobileServer:
)
case self.opcodes.MSG_TYPING:
await self.auth_required(
userPhone, self.processors.msg_typing, payload, seq, writer, userId
userPhone,
self.processors.msg_typing,
payload,
seq,
writer,
userId,
)
case self.opcodes.CONTACT_INFO:
await self.auth_required(
userPhone, self.processors.contact_info, payload, seq, writer
userPhone,
self.processors.contact_info,
payload,
seq,
writer,
)
case self.opcodes.COMPLAIN_REASONS_GET:
await self.auth_required(
userPhone, self.processors.complain_reasons_get, payload, seq, writer
userPhone,
self.processors.complain_reasons_get,
payload,
seq,
writer,
)
case self.opcodes.PROFILE:
await self.processors.profile(
@@ -161,12 +265,18 @@ class OnemeMobileServer:
)
case self.opcodes.CHAT_SUBSCRIBE:
await self.auth_required(
userPhone, self.processors.chat_subscribe, payload, seq, writer
userPhone,
self.processors.chat_subscribe,
payload,
seq,
writer,
)
case _:
self.logger.warning(f"Неизвестный опкод {opcode}")
except Exception as e:
self.logger.error(f"Произошла ошибка при работе с клиентом {address[0]}:{address[1]}: {e}")
self.logger.error(
f"Произошла ошибка при работе с клиентом {address[0]}:{address[1]}: {e}"
)
traceback.print_exc()
# Удаляем клиента из словаря
@@ -174,7 +284,9 @@ class OnemeMobileServer:
await self._end_session(userId, address[0], address[1])
writer.close()
self.logger.info(f"Прекратил работать работать с клиентом {address[0]}:{address[1]}")
self.logger.info(
f"Прекратил работать работать с клиентом {address[0]}:{address[1]}"
)
async def _finish_auth(self, writer, addr, phone, id):
"""Завершение открытия сессии"""
@@ -184,12 +296,7 @@ class OnemeMobileServer:
# Добавляем новое подключение в словарь
if user:
user["clients"].append(
{
"writer": writer,
"ip": addr[0],
"port": addr[1],
"protocol": "oneme"
}
{"writer": writer, "ip": addr[0], "port": addr[1], "protocol": "oneme"}
)
else:
self.clients[id] = {
@@ -200,9 +307,9 @@ class OnemeMobileServer:
"writer": writer,
"ip": addr[0],
"port": addr[1],
"protocol": "oneme"
"protocol": "oneme",
}
]
],
}
async def _end_session(self, id, ip, port):
@@ -229,4 +336,4 @@ class OnemeMobileServer:
self.logger.info(f"Сокет запущен на порту {self.port}")
async with self.server:
await self.server.serve_forever()
await self.server.serve_forever()