fast_down/http/
prefetch.rs1use crate::{
2 UrlInfo,
3 http::{GetResponse, HttpClient, HttpError, HttpHeaders, HttpRequestBuilder, HttpResponse},
4 url_info::FileId,
5};
6use content_disposition;
7use std::{borrow::Borrow, future::Future};
8use url::Url;
9
10pub trait Prefetch<Client: HttpClient> {
11 type Error;
12 fn prefetch(
13 &self,
14 url: Url,
15 ) -> impl Future<Output = Result<(UrlInfo, GetResponse<Client>), Self::Error>> + Send;
16}
17
18impl<Client: HttpClient, BorrowClient: Borrow<Client> + Sync> Prefetch<Client> for BorrowClient {
19 type Error = HttpError<Client>;
20 async fn prefetch(&self, url: Url) -> Result<(UrlInfo, GetResponse<Client>), Self::Error> {
21 prefetch(self.borrow(), url).await
22 }
23}
24
25fn get_filename(headers: &impl HttpHeaders, url: &Url) -> String {
26 headers
27 .get("content-disposition")
28 .ok()
29 .and_then(|s| content_disposition::parse_content_disposition(s).filename_full())
30 .map(|s| urlencoding::decode(&s).map(String::from).unwrap_or(s))
31 .filter(|s| !s.trim().is_empty())
32 .or_else(|| {
33 url.path_segments()
34 .and_then(|mut segments| segments.next_back())
35 .map(|s| urlencoding::decode(s).unwrap_or(s.into()))
36 .filter(|s| !s.trim().is_empty())
37 .map(|s| s.to_string())
38 })
39 .unwrap_or_else(|| url.to_string())
40}
41
42async fn prefetch<Client: HttpClient>(
43 client: &Client,
44 url: Url,
45) -> Result<(UrlInfo, GetResponse<Client>), HttpError<Client>> {
46 let resp = client
47 .get(url, None)
48 .send()
49 .await
50 .map_err(HttpError::Request)?;
51 let headers = resp.headers();
52 let supports_range = headers
53 .get("accept-ranges")
54 .map(|v| v == "bytes")
55 .unwrap_or(false);
56 let size = headers
57 .get("content-length")
58 .ok()
59 .and_then(|v| v.parse().ok())
60 .unwrap_or(0);
61 let final_url = resp.url();
62 Ok((
63 UrlInfo {
64 final_url: final_url.clone(),
65 name: get_filename(headers, final_url),
66 size,
67 supports_range,
68 fast_download: size > 0 && supports_range,
69 file_id: FileId::new(headers.get("etag").ok(), headers.get("last-modified").ok()),
70 },
71 resp,
72 ))
73}