Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở Phù hợp với người mới
#570 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
88/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend

Hướng nghiên cứu

Bắt đầu trong common/src/binance_common/websocket.py với WebSocketStreamBase: tìm các map ở mức module global_stream_connections và global_user_stream_connections, rồi mọi chỗ chúng được đọc hoặc ghi — bộ lọc subscribe, on, hủy đăng ký và dọn dẹp khi kết nối lại — và chuyển chúng thành thuộc tính theo instance được khởi tạo trong __init__. Chạy trước script tái tạo aiohttp tự chứa trong phần thân issue để xác nhận đầu ra sai, rồi chạy lại để kiểm tra rằng mỗi server nhận SUBSCRIBE riêng của nó và fut_cb nhận {'venue': 'futures'}. Kiểm tra các test websocket hiện có của repo vẫn pass, vì logic pooling và khử trùng lặp phụ thuộc vào registry này.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
Python
Star
2.9k
Fork
708
Merge trung bình
2 giờ 17 phút
Pull request đã merge (30 ngày)
2

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của binance/binance-connector-python

Tất cả issue của binance/binance-connector-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.