mod download_file;
pub use download_file::*;
use crate::utils::io::WithProgress;
use crate::utils::reqwest_resume;
use anyhow::Context as _;
use futures::TryStreamExt;
use reqwest::Response;
use std::net::SocketAddr;
use std::sync::{Arc, LazyLock};
use tap::Pipe;
use tokio::io::AsyncBufRead;
use tokio::net::TcpListener;
use tokio_util::either::Either::{Left, Right};
use tracing::info;
use url::Url;
const MIN_LISTEN_BACKLOG: u32 = 4096;
pub async fn bind_tcp_listener(addr: SocketAddr, backlog: u32) -> anyhow::Result<TcpListener> {
let socket = if addr.is_ipv6() {
tokio::net::TcpSocket::new_v6()
} else {
tokio::net::TcpSocket::new_v4()
}
.with_context(|| format!("could not create TCP socket for {addr}"))?;
let _ = socket.set_reuseaddr(true);
socket
.bind(addr)
.with_context(|| format!("could not bind to {addr}"))?;
socket
.listen(backlog.max(MIN_LISTEN_BACKLOG))
.with_context(|| format!("could not listen on {addr}"))
}
pub fn global_http_client() -> reqwest::Client {
static CLIENT: LazyLock<reqwest::Client> = LazyLock::new(reqwest::Client::new);
CLIENT.clone()
}
pub async fn reader(
location: &str,
option: DownloadFileOption,
callback: Option<Arc<dyn Fn(String) + Sync + Send>>,
) -> anyhow::Result<impl AsyncBufRead> {
let (stream, content_length) = match Url::parse(location) {
Ok(url) => {
info!("Downloading file: {}", url);
match option {
DownloadFileOption::Resumable => {
let resume_resp = reqwest_resume::get(url).await?;
let resp = resume_resp.response().error_for_status_ref()?;
let content_length = resp.content_length().unwrap_or_default();
let stream = resume_resp
.bytes_stream()
.map_err(std::io::Error::other)
.pipe(tokio_util::io::StreamReader::new);
(Left(Left(stream)), content_length)
}
DownloadFileOption::NonResumable => {
let resp = global_http_client()
.get(url)
.send()
.await?
.error_for_status()?;
let content_length = resp.content_length().unwrap_or_default();
let stream = resp
.bytes_stream()
.map_err(std::io::Error::other)
.pipe(tokio_util::io::StreamReader::new);
(Left(Right(stream)), content_length)
}
}
}
Err(_) => {
info!("Reading file: {}", location);
let stream = tokio::fs::File::open(location).await?;
let content_length = stream.metadata().await?.len();
(Right(stream), content_length)
}
};
const DOWNLOAD_BUFFER_SIZE: usize = 512 * 1024;
Ok(tokio::io::BufReader::with_capacity(
DOWNLOAD_BUFFER_SIZE,
WithProgress::wrap_sync_read_with_callback("Loading", stream, content_length, callback)
.bytes(),
))
}
pub async fn http_get(url: &Url) -> anyhow::Result<Response> {
info!(%url, "GET");
Ok(global_http_client()
.get(url.clone())
.send()
.await?
.error_for_status()?)
}