mtproto_proxy/doc/handler-downstream-flow.md
Sergey Prokhorov 4e0db66fc1
mtp_config: parse default DC id from Telegram config; drop get_netloc_safe; ETS protected
- parse_config/1 now returns {DefaultDc, Downstreams} using a single
  lists:foldl pass; the 'default X;' line sets the fallback DC id
- update_ids/3 stores {default_dc, DcId} in ETS alongside dc_ids
- get_default_dc/0 reads default_dc from ETS (safe from any process)
- get_downstream_safe/2 fallback uses get_default_dc() instead of
  random_choice; errors immediately if default == requested (avoid loop)
- get_netloc_safe/1 removed: dead code since 2018, never called
- ETS table changed from public to protected (only mtp_config writes)
- doc/handler-downstream-flow.md: new sequence diagram + update note
  about pool resolution fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 18:35:02 +02:00

1.7 KiB

Handler ↔ downstream lookup and handshake

Shows how mtp_handler locates an mtp_down_conn for a new client connection and the steady-state data flow that follows.

Key actors:

  • mtp_handler — one process per Telegram client TCP connection
  • mtp_dc_pool — manages a pool of downstream connections for one DC
  • mtp_down_conn — multiplexed TCP connection to a Telegram DC
  • Telegram DC — the upstream Telegram data-centre server
sequenceDiagram
    participant Client as Telegram client
    participant Handler as mtp_handler
    participant Pool as mtp_dc_pool
    participant Down as mtp_down_conn
    participant TG as Telegram DC

    Client->>Handler: TCP connect + Hello bytes

    Note over Handler: decode protocol headers<br/>(fake-TLS / obfuscated / secure)<br/>stage: hello → tunnel

    Note over Handler: resolve pool: whereis(dc_to_pool_name(DcId))<br/>(registered name lookup; falls back to default DC from mtp_config if not found)
    Handler->>Pool: mtp_dc_pool:get(Pool, self(), Opts) [sync]
    Pool-->>Down: upstream_new(Handler, Opts) [cast]
    Pool->>Handler: Downstream pid

    Note over Handler: down = Downstream<br/>stage = tunnel

    loop steady-state data exchange
        Client->>Handler: TCP data
        Handler->>Down: mtp_down_conn:send(Down, Data) [sync]
        Down->>TG: TCP data (RPC-framed)
        TG->>Down: TCP data
        Down->>Handler: ok
        Down-->>Handler: {proxy_ans, Down, Data} [cast]
        Handler->>Client: TCP data
        Handler-->>Down: mtp_down_conn:ack(Down, Count, Size) [cast]
    end

    Client->>Handler: TCP close
    Handler-->>Pool: mtp_dc_pool:return(Pool, self()) [cast]
    Pool-->>Down: upstream_closed(Down, Handler) [cast]