Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[Bug] WebSocket Streams: stream registry is global, two clients with the same stream name get each other's data

Aperta Adatta ai principianti
#570 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
88/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
python
Ambito
backend

Direzione di ricerca

Inizia in common/src/binance_common/websocket.py con WebSocketStreamBase: trova le mappe a livello di modulo global_stream_connections e global_user_stream_connections, poi ogni punto in cui vengono lette o scritte — il filtro di subscribe, on, l'unsubscribe e la pulizia durante la riconnessione — e spostale in attributi per istanza inizializzati in __init__. Esegui prima lo script di riproduzione aiohttp autosufficiente riportato nel corpo dell'issue per confermare l'output errato, poi rieseguilo per verificare che ogni server riceva il proprio SUBSCRIBE e che fut_cb riceva {'venue': 'futures'}. Controlla che i test websocket esistenti del repo passino ancora, poiché la logica di pooling e deduplicazione dipende da questo registro.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

WebSocketStreamBase keeps its stream-to-connection registry in two module-level objects (global_stream_connections, global_user_stream_connections in common/src/binance_common/websocket.py). Every client in the process shares them, keyed only by stream name.

Spot and USDⓈ-M Futures use the same stream names (btcusdt@aggTrade, btcusdt@depth, ...), and so do mainnet and testnet. When two stream clients in one process subscribe to the same name:

  • the second client never sends SUBSCRIBE, because the name is already registered;
  • its callback is attached to the first client's connection, so it receives the other venue's data, with no error.

Reproduce

Self-contained: one local aiohttp server with two endpoints standing in for two venues; each tags its messages with its name. binance-common==4.5.1 (same as master at 43f19a9), Python 3.13.

import asyncio, json, logging
from aiohttp import web
from binance_common.configuration import ConfigurationWebSocketStreams
from binance_common.websocket import WebSocketStreamBase, RequestStream
logging.disable(logging.CRITICAL)
subs = {"spot": [], "futures": []}
def mk(name):
    async def handler(request):
        ws = web.WebSocketResponse(); await ws.prepare(request)
        async for msg in ws:
            d = json.loads(msg.data)
            if d["method"] == "SUBSCRIBE":
                subs[name] += d["params"]
                await ws.send_json({"id": d["id"], "result": None})
                for s in d["params"]:
                    await ws.send_json({"stream": s, "data": {"venue": name}})
        return ws
    return handler
async def main():
    app = web.Application()
    app.router.add_get("/spot/stream", mk("spot")); app.router.add_get("/fut/stream", mk("futures"))
    r = web.AppRunner(app); await r.setup(); await web.TCPSite(r, "127.0.0.1", 18766).start()
    spot = WebSocketStreamBase(ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18766/spot/stream"))
    fut = WebSocketStreamBase(ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18766/fut/stream"))
    await spot.create_connection(); await fut.create_connection()
    got = {"spot_cb": [], "fut_cb": []}
    h1 = await RequestStream(spot, "btcusdt@aggTrade"); h1.on("message", lambda d: got["spot_cb"].append(d))
    h2 = await RequestStream(fut, "btcusdt@aggTrade");  h2.on("message", lambda d: got["fut_cb"].append(d))
    await asyncio.sleep(0.5)
    print("SUBSCRIBE frames received by servers:", subs)
    print("callback payloads:", got)
    await spot.close_connection(); await fut.close_connection(); await r.cleanup()
asyncio.run(main())

Output:

SUBSCRIBE frames received by servers: {'spot': ['btcusdt@aggTrade'], 'futures': []}
callback payloads: {'spot_cb': [{'stream': 'btcusdt@aggTrade', 'data': {'venue': 'spot'}}], 'fut_cb': [{'stream': 'btcusdt@aggTrade', 'data': {'venue': 'spot'}}]}

Expected

Each server receives its own SUBSCRIBE, and fut_cb receives {'venue': 'futures'}.

Impact

A process that follows the same symbol on Spot and Futures (basis or arbitrage monitoring, for example) gets Spot trades delivered to the Futures handler. Nothing is logged, and the payload shapes are close enough that it is easy to miss.

Suggested fix

Make both maps instance attributes created in the client's __init__ and use them wherever the globals are referenced today (subscribe filter, on, unsubscribe and the reconnect cleanup). Streams would then be deduplicated per client, which is what the pooling logic needs.

Lingua principale
Python
Stelle
2.9k
Fork
707
Merge medio
2h 17m
PR unite (30g)
2

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di binance/binance-connector-python

Tutte le issue di binance/binance-connector-python

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.