1use std::sync::Arc;
13
14use didbot_http::PrivateAddresses;
15use didbot_lookup::{ForeignRecords, Limits, LocalRepositories};
16use didbot_pds::Registry;
17
18struct Hosted(Arc<dyn Registry>);
20
21impl LocalRepositories for Hosted {
22 fn holds(&self, did: &str) -> bool {
23 self.0.account(did).is_some()
24 }
25
26 fn serves(&self, host: &str) -> bool {
27 serves(self.0.as_ref(), host)
28 }
29}
30
31pub(crate) fn serves(registry: &dyn Registry, host: &str) -> bool {
34 if registry.did_document(host).is_some() || registry.handle_did(host).is_some() {
35 return true;
36 }
37 registry
41 .available_user_domains()
42 .iter()
43 .any(|domain| host.ends_with(domain.as_str()) || host == domain.trim_start_matches('.'))
44}
45
46pub(crate) fn reader(
48 registry: Arc<dyn Registry>,
49 private: PrivateAddresses,
50) -> Arc<ForeignRecords> {
51 Arc::new(ForeignRecords::over_network(
52 Arc::new(Hosted(registry)),
53 private,
54 Limits::default(),
55 ))
56}
57
58pub(crate) enum Document {
60 Found(serde_json::Value),
62 NotFound,
64 Gone,
67}
68
69pub(crate) enum DocumentError {
71 Address(didbot_http::AddressError),
74 Failed(String),
77}
78
79pub(crate) async fn fetch_document(
85 url: &url::Url,
86 did: &str,
87 private: PrivateAddresses,
88) -> Result<Document, DocumentError> {
89 let client = didbot_http::pinned_client(url, private)
90 .await
91 .map_err(DocumentError::Address)?;
92 let response = client
93 .get(url.clone())
94 .send()
95 .await
96 .map_err(|error| DocumentError::Failed(format!("fetching {url}: {error}")))?;
97 match response.status() {
98 reqwest::StatusCode::NOT_FOUND => return Ok(Document::NotFound),
99 reqwest::StatusCode::GONE => return Ok(Document::Gone),
100 status if !status.is_success() => {
101 return Err(DocumentError::Failed(format!(
102 "fetching {url}: it answered {status}"
103 )))
104 }
105 _ => {}
106 }
107 let body = didbot_http::read_bounded(response, didbot_http::MAX_JSON_BODY)
108 .await
109 .map_err(|error| DocumentError::Failed(format!("reading {url}: {error}")))?;
110 let document: serde_json::Value = serde_json::from_slice(&body)
111 .map_err(|error| DocumentError::Failed(format!("parsing {url}: {error}")))?;
112 if document.get("id").and_then(serde_json::Value::as_str) != Some(did) {
115 return Err(DocumentError::Failed(format!(
116 "the document at {url} is about {:?}",
117 document.get("id")
118 )));
119 }
120 Ok(Document::Found(document))
121}