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
- You call
upload.getFileon the home DC. - If the file lives on a CDN DC, the server returns
upload.fileCdnRedirectcontaining:dc_id- which CDN DC to talk tofile_token- opaque credential forupload.getCdnFileencryption_key(32 bytes AES-256-CTR key)encryption_iv(16 bytes)
- Connect to the CDN DC with
CdnDownloader::connect. - Call
download_chunk_raw,download_all, ordownload_all_with_reupload. - CDN DCs use AES-256-CTR (not AES-IGE).
CdnDownloaderdecrypts 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 therequest_token; callupload.reuploadCdnFileon 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());
}