//! Network-specific API. use std::time::Duration; pub use self::download::{download_file, download_file_stream, Download}; pub(crate) use self::{ request::{request_json, request_multipart}, telegram_response::TelegramResponse, }; mod download; mod request; mod telegram_response; /// The default Telegram API URL. pub const TELEGRAM_API_URL: &str = "https://api.telegram.org"; /// Constructs a network client from the `TELOXIDE_PROXY` environmental /// variable. /// /// This function passes the value of `TELOXIDE_PROXY` into /// [`reqwest::Proxy::all`], if it exists, otherwise returns the default /// client. /// /// ## Note /// /// The created client will have safe settings, meaning that it will be able to /// work in long time durations, see the [issue 223]. /// /// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 /// /// ## Panics /// /// If `TELOXIDE_PROXY` exists, but isn't correct url. pub fn client_from_env() -> reqwest::Client { use reqwest::Proxy; const TELOXIDE_PROXY: &str = "TELOXIDE_PROXY"; let builder = default_reqwest_settings(); match std::env::var(TELOXIDE_PROXY).ok() { Some(proxy) => builder.proxy(Proxy::all(&proxy).expect("reqwest::Proxy creation failed")), None => builder, } .build() .expect("creating reqwest::Client") } /// Returns a reqwest client builder with default settings. /// /// Client built from default settings is supposed to work in long time /// durations, see the [issue 223]. /// /// Current setting are: /// - `connection/keep-alive` default header /// - connection timeout of 5 seconds /// - timeout of 17 seconds /// - tcp_nodelay is on /// /// Notes: /// 1. the settings may change in the future /// 2. if you are using polling mechanizm to get updates, the timeout configured /// in the client should be bigger than polling timeout /// /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 pub fn default_reqwest_settings() -> reqwest::ClientBuilder { use reqwest::header::{HeaderMap, CONNECTION}; let mut headers = HeaderMap::new(); headers.insert(CONNECTION, "keep-alive".parse().unwrap()); let connect_timeout = Duration::from_secs(5); let timeout = connect_timeout + Duration::from_secs(12); reqwest::Client::builder() .connect_timeout(connect_timeout) .timeout(timeout) .tcp_nodelay(true) .default_headers(headers) } /// Creates URL for making HTTPS requests. See the [Telegram documentation]. /// /// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Url { base.join(&format!( "/bot{token}/{method}", token = token, method = method_name )) .expect("failed to format url") } /// Creates URL for downloading a file. See the [Telegram documentation]. /// /// [Telegram documentation]: https://core.telegram.org/bots/api#file fn file_url(base: reqwest::Url, token: &str, file_path: &str) -> reqwest::Url { base.join(&format!( "file/bot{token}/{file}", token = token, file = file_path )) .expect("failed to format url") } #[cfg(test)] mod tests { use crate::net::*; #[test] fn method_url_test() { let url = method_url( reqwest::Url::parse(TELEGRAM_API_URL).unwrap(), "535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao", "methodName", ); assert_eq!( url.as_str(), "https://api.telegram.org/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/methodName" ); } #[test] fn file_url_test() { let url = file_url( reqwest::Url::parse(TELEGRAM_API_URL).unwrap(), "535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao", "AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ", ); assert_eq!( url.as_str(), "https://api.telegram.org/file/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ" ); } }