Skip to main content

lychee_lib/types/
resolver.rs

1use super::{FileType, InputContent, ResolvedInputSource};
2use crate::utils::request;
3use crate::{BasicAuthExtractor, ErrorKind, Result, Uri};
4use http::HeaderMap;
5use reqwest::{Client, Request, Url};
6
7/// Structure to fetch remote content.
8#[derive(Debug, Default, Clone)]
9pub struct UrlContentResolver {
10    pub basic_auth_extractor: Option<BasicAuthExtractor>,
11    pub headers: HeaderMap,
12    pub client: reqwest::Client,
13}
14
15impl UrlContentResolver {
16    /// Fetch remote content by URL.
17    ///
18    /// This method is not intended to check if a URL is functional but
19    /// to get a URL's content and process the content.
20    pub async fn url_contents(&self, url: Url) -> Result<InputContent> {
21        // Assume HTML for default paths
22        let file_type = match url.path() {
23            path if path.is_empty() || path == "/" => FileType::Html,
24            _ => FileType::from(url.as_str()),
25        };
26
27        let credentials = request::extract_credentials(
28            self.basic_auth_extractor.as_ref(),
29            &Uri { url: url.clone() },
30        );
31
32        let request = self.build_request(&url, credentials)?;
33        let content = get_request_body_text(&self.client, request).await?;
34
35        let input_content = InputContent {
36            source: ResolvedInputSource::RemoteUrl(Box::new(url.clone())),
37            file_type,
38            content,
39        };
40
41        Ok(input_content)
42    }
43
44    fn build_request(
45        &self,
46        url: &Url,
47        credentials: Option<super::BasicAuthCredentials>,
48    ) -> Result<Request> {
49        let mut request = self
50            .client
51            .request(reqwest::Method::GET, url.clone())
52            .build()
53            .map_err(ErrorKind::BuildRequestClient)?;
54
55        request.headers_mut().extend(self.headers.clone());
56        if let Some(credentials) = credentials {
57            credentials.append_to_request(&mut request);
58        }
59
60        Ok(request)
61    }
62}
63
64async fn get_request_body_text(client: &Client, request: Request) -> Result<String> {
65    client
66        .execute(request)
67        .await
68        .map_err(ErrorKind::NetworkRequest)?
69        .text()
70        .await
71        .map_err(ErrorKind::ReadResponseBody)
72}