Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CDN Downloads

Large files on Telegram are often served from CDN DCs - lightweight edge data-centres that don’t participate in the normal MTProto auth flow. ferogram exposes the full CDN download path via CdnDownloader in ferogram::cdn_download.

In normal usage you never need to interact with CdnDownloader directly - client.download, client.download_file, and iter_download handle CDN redirects transparently. This page is for advanced use-cases where you need explicit control.


How CDN redirects work

  1. You call upload.getFile on the home DC.
  2. If the file lives on a CDN DC, the server returns upload.fileCdnRedirect containing:
    • dc_id - which CDN DC to talk to
    • file_token - opaque credential for upload.getCdnFile
    • encryption_key (32 bytes AES-256-CTR key)
    • encryption_iv (16 bytes)
  3. Connect to the CDN DC with CdnDownloader::connect.
  4. Call download_chunk_raw, download_all, or download_all_with_reupload.
  5. CDN DCs use AES-256-CTR (not AES-IGE). CdnDownloader decrypts transparently.

Constants

#![allow(unused)]
fn main() {
use ferogram::cdn_download::CDN_CHUNK_SIZE;

// 131072 bytes  -  CDN DCs require exactly 128 KB fixed-size parts
// so the offset → hash mapping in upload.getCdnFileHashes stays consistent.
assert_eq!(CDN_CHUNK_SIZE, 128 * 1024);
}

CdnDownloader

Construction

async CdnDownloader::connect(cdn_dc_addr: &str, cdn_dc_id: i16, file_token: Vec<u8>, encryption_key: [u8; 32], encryption_iv: [u8; 16], socks5: Option<&Socks5Config>) → Result<Self, InvocationError>
Open a fresh connection to the CDN DC at cdn_dc_addr (format: "ip:port") and return a ready downloader. Uses obfuscated transport. Pass the proxy config from your Client setup if one is active.
sync CdnDownloader::new(conn: DcConnection, file_token: Vec<u8>, encryption_key: [u8; 32], encryption_iv: [u8; 16]) → Self
Wrap an already-open DcConnection. Useful if you manage connection pooling yourself.

Downloading

async cdn.download_chunk_raw(byte_offset: i64, limit: i32) → Result<CdnChunkResult, InvocationError>
Download and AES-CTR-decrypt a single chunk at byte_offset with limit bytes. Use CDN_CHUNK_SIZE as the limit.

Returns one of:

  • CdnChunkResult::Data(Vec<u8>) - decrypted bytes.
  • CdnChunkResult::ReuploadNeeded(Vec<u8>) - server wants the file reuploaded to it first. Contains the request_token; call upload.reuploadCdnFile on the main DC then retry.
async cdn.download_all(total_size: Option<i64>) → Result<Vec<u8>, InvocationError>
Download and reassemble the full file. If ReuploadNeeded is encountered, returns an error - use download_all_with_reupload instead if the file might need reuploading.

Pass total_size = Some(n) for pre-allocation; None is fine too.

async cdn.download_all_with_reupload<F, Fut>(total_size: Option<i64>, reupload_fn: F) → Result<Vec<u8>, InvocationError>
Like download_all but handles ReuploadNeeded automatically. The reupload_fn closure receives the request_token bytes and must call upload.reuploadCdnFile on the main DC, then return Ok(()). The downloader retries the chunk after the reupload completes.
#![allow(unused)]
fn main() {
let bytes = cdn.download_all_with_reupload(
    Some(file_size),
    |request_token| async move {
        let body = serialize_reupload_cdn_file(&file_token, &request_token);
        main_dc_conn.rpc_call_raw(&body).await?;
        Ok(())
    },
).await?;
}

Low-level TL helpers

These are public for callers who need to build raw requests manually:

#![allow(unused)]
fn main() {
use ferogram::cdn_download::{serialize_get_cdn_file, serialize_reupload_cdn_file};

// Build an upload.getCdnFile#395f69da payload
let req_bytes = serialize_get_cdn_file(&file_token, byte_offset, CDN_CHUNK_SIZE);

// Build an upload.reuploadCdnFile payload
let reup_bytes = serialize_reupload_cdn_file(&file_token, &request_token);
}

Full example

#![allow(unused)]
fn main() {
use ferogram::cdn_download::{CdnDownloader, CDN_CHUNK_SIZE};

// 1. Detect the CDN redirect from an upload.getFile call (raw API)
// ...

// 2. Connect to the CDN DC
let mut cdn = CdnDownloader::connect(
    "149.154.167.222:443",  // CDN DC address from the redirect
    5,                       // CDN DC ID
    file_token,
    encryption_key,
    encryption_iv,
    None,  // no SOCKS5
).await?;

// 3. Download everything
let bytes = cdn.download_all(Some(total_size)).await?;

println!("Downloaded {} bytes", bytes.len());
}