@dert2313

Почему websocket соединение сразу обрывается?

У меня есть маленькое приложение на 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
  • Вопрос задан
  • 819 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы