У меня есть маленькое приложение на fastapi, в нём есть ендпоинт на websocket, а там цикл while True.
Все это в одном скрипте, и ,собственно, при подключении через вебсокет на другие эндпоинты приложение не отвечает, попробовал перенести в другой скрипт, тогда соединение сразу обрывается с кодом 403.
Пока только догадываюсь, что сервер не принимает запросы, когда существует активное вебсокет соединение(сервер uvicorn).
from typing import List
import time
from sqlalchemy import and_, or_
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, Response
from models import Offer, Battle, BattlesLogs, Accept, session
from pydantic_models import (OfferGetModel,
OfferResponseModel, AcceptModel,
BattleStartModel, BattleMoveModel)
from utils import check_winner_and_loser, damage_loser, user_can_get_damage
app = FastAPI()
@app.post("/battles_create")
async def battles_create(offer: OfferGetModel):
session_db = session()
offer = Offer(ntfId=offer.ntfId, userId=offer.userId)
session_db.add(offer)
session_db.commit()
session_db.expunge_all()
session_db.close()
return {"offer": OfferResponseModel.from_orm(offer)}
@app.get("/battles_list")
async def battles_list():
session_db = session()
offers = [OfferResponseModel.from_orm(offer) for offer in session_db.query(Offer).all()]
session_db.expunge_all()
session_db.close()
return {"offers": offers}
@app.post("/battles/accept")
async def battles_accept(accept: AcceptModel):
session_db = session()
offer = session_db.query(Offer).get(accept.offerId)
if offer is None:
raise HTTPException(
status_code=404,
detail="Offer with this offerId does not exists"
)
if session_db.query(Accept).filter(
and_(
Accept.userId == accept.userId,
Accept.nftId == accept.nftId,
Accept.offerId == accept.offerId
)
).count() != 0:
raise HTTPException(
status_code=409,
detail="Accept with this userId and nftId and offerId is already exists"
)
accept = Accept(userId=accept.userId, nftId=accept.nftId, offerId=accept.offerId)
session_db.add(accept)
session_db.commit()
offer.accepts.append(accept)
session_db.expunge_all()
session_db.close()
return {"accept": accept}
@app.post("/battles_start")
async def battles_start(battle: BattleStartModel):
session_db = session()
offer = session_db.query(Offer).get(battle.offerId)
accept = session_db.query(Accept).get(battle.acceptId)
if offer is None:
raise HTTPException(
status_code=404,
detail="Offer or accept with this offerId does not exists"
)
if session_db.query(Battle).filter(
and_(
Battle.acceptId == battle.acceptId,
Battle.offerId == battle.offerId
)
).count() != 0:
raise HTTPException(
status_code=409,
detail="Battle with this acceptId and offerId is already exists"
)
battle = Battle(
offerId=battle.offerId,
acceptId=battle.acceptId,
offerUserId=offer.userId,
acceptUserId=accept.userId
)
session_db.add(battle)
session_db.commit()
session_db.expunge_all()
session_db.close()
return {"battle": BattleStartModel.from_orm(battle)}
@app.post("/battles_move")
async def battles_move(battle_move: BattleMoveModel):
session_db = session()
if session_db.query(Battle).filter(
or_(
Battle.offerUserId == battle_move.userId,
Battle.acceptUserId == battle_move.userId
)
).count() == 0:
raise HTTPException(
status_code=409,
detail="You should being in battle to make move"
)
if session_db.query(BattlesLogs).filter(
and_(
BattlesLogs.userId == battle_move.userId,
BattlesLogs.idBattle == battle_move.idBattle,
BattlesLogs.choice == battle_move.choice,
BattlesLogs.round == battle_move.round
)
).count() != 0:
raise HTTPException(
status_code=409,
detail="You already make this move"
)
battle_log = BattlesLogs(
userId=battle_move.userId,
idBattle=battle_move.idBattle,
choice=battle_move.choice,
round=battle_move.round
)
session_db.add(battle_log)
session_db.commit()
session_db.expunge_all()
session_db.close()
return Response(status_code=200)
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, data: dict):
for connection in self.active_connections:
await connection.send_json(data)
connection_manager = ConnectionManager()
result_to_send = {"results": []}
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await connection_manager.connect(websocket)
try:
while True:
session_db = session()
battles = session_db.query(Battle).all()
for battle in battles:
battles_logs_with_need_battleId = session_db.query(
BattlesLogs
).filter(BattlesLogs.idBattle == battle.battleId)
if battles_logs_with_need_battleId.count() == 2:
first_user_move = battles_logs_with_need_battleId[0]
second_user_move = battles_logs_with_need_battleId[1]
winner_id, loser_id = check_winner_and_loser(first_user_move, second_user_move)
if winner_id is not None:
damage_loser(battle, loser_id)
result = {"battles_round": {
"battleId": battle.battleId,
"firstUserId": first_user_move.userId,
"secondUserId": second_user_move.userId,
"winner": winner_id
}}
else:
result = {"battles_round": {
"battleId": battle.battleId,
"firstUserId": first_user_move.userId,
"secondUserId": second_user_move.userId,
"winner": None
}}
if not user_can_get_damage(battle, loser_id):
rez = {"battles_finish": {
"battleId": battle.battleId,
"firstUserId": first_user_move.userId,
"secondUserId": second_user_move.userId,
"winner": winner_id
}}
result_to_send["results"].append(rez)
battle.delete()
result_to_send["results"].append(result)
battles_logs_with_need_battleId.delete()
session_db.commit()
await connection_manager.broadcast(result_to_send)
else:
continue
session_db.expunge_all()
session_db.close()
time.sleep(5)
except WebSocketDisconnect:
connection_manager.disconnect(websocket)
except:
pass