pubhubs/client/
discovery.rs1use crate::api::{self, ApiResultExt as _, NoPayload};
2use crate::servers::constellation::ConstellationOrId;
3use crate::servers::{self, Constellation, server::Server as _};
4
5impl crate::client::Client {
6 pub async fn get_constellation(&self, url: &url::Url) -> anyhow::Result<ConstellationOrId> {
8 crate::misc::task::retry(|| async {
9 (match self.query::<api::DiscoveryInfo>(url, NoPayload)
12 .await
13 .retryable()?
14 {
15 Some(inf) => Ok(inf.constellation_or_id),
16 None => Ok(None),
17 }) as anyhow::Result<Option<ConstellationOrId>>
18 })
19 .await?
20 .ok_or_else(|| {
21 anyhow::anyhow!(
22 "timeout waiting for {url} to publish constellation",
23 url = url
24 )
25 })
26 }
27
28 pub async fn try_get_stable_constellation(
31 &self,
32 phc_url: &url::Url,
33 ) -> api::Result<Option<Constellation>> {
34 log::debug!("trying to get stable constellation");
35 let phc_inf = self.query::<api::DiscoveryInfo>(phc_url, NoPayload).await?;
36 if phc_inf.constellation_or_id.is_none() {
37 log::debug!(
38 "{phc}'s constellation not yet set",
39 phc = servers::Name::PubhubsCentral
40 );
41 return Ok(None);
42 }
43
44 let Some(constellation) = phc_inf.constellation_or_id.unwrap().into_constellation() else {
45 log::error!(
46 "{phc} did not return a constellation, but just its id",
47 phc = servers::Name::PubhubsCentral
48 );
49 return Err(api::ErrorCode::InternalError);
50 };
51
52 let mut js = tokio::task::JoinSet::new();
53
54 macro_rules! get_constellation_from_server {
55 ($server:ident) => {
56 let server_name = crate::servers::$server::Server::NAME;
57 if server_name != servers::Name::PubhubsCentral {
58 let url = constellation.url(server_name);
59 js.spawn_local(
60 self.query::<api::DiscoveryInfo>(url, NoPayload)
61 .into_future(),
62 );
63 }
64 };
65 }
66
67 crate::for_all_servers!(get_constellation_from_server);
68
69 'lp: loop {
70 match js.join_next().await {
71 None => break 'lp,
72 Some(Ok(inf_res)) => {
73 let inf = inf_res?;
74
75 if inf.constellation_or_id.is_none()
76 || *inf.constellation_or_id.unwrap().id() != constellation.id
77 {
78 log::debug!("constellations not yet in sync");
79 return Ok(None);
80 }
81 }
82 Some(Err(join_err)) => {
83 log::warn!(
84 "unexpected join error getting constellation from server: {join_err}"
85 );
86 return Err(api::ErrorCode::InternalError);
87 }
88 }
89 }
90
91 log::info!("obtained stable constellation");
92 Ok(Some(constellation))
93 }
94}
95
96pub struct DiscoveryInfoCheck<'a> {
98 pub phc_url: &'a url::Url,
99 pub name: crate::servers::Name,
100 pub self_check_code: Option<&'a str>,
101 pub constellation: Option<&'a Constellation>,
102}
103
104impl DiscoveryInfoCheck<'_> {
105 pub fn check(
108 self,
109 inf: api::DiscoveryInfoResp,
110 source: &url::Url,
111 ) -> api::Result<api::DiscoveryInfoResp> {
112 if inf.name != self.name {
113 log::error!(
114 "supposed {} at {} returned name {}",
115 self.name,
116 source,
117 inf.name
118 );
119 return Err(api::ErrorCode::InternalError);
120 }
121
122 if &inf.phc_url != self.phc_url {
123 log::error!(
124 "{} at {} thinks PubHubs Central is at {}",
125 self.name,
126 source,
127 inf.phc_url,
128 );
129 return Err(api::ErrorCode::InternalError);
130 }
131
132 if let Some(scc) = self.self_check_code
133 && inf.self_check_code != scc
134 {
135 log::error!(
136 "{} at {} is not me! (Different self_check_code.)",
137 self.name,
138 source,
139 );
140 return Err(api::ErrorCode::InternalError);
141 }
142
143 if inf.master_enc_key_part.is_some()
144 != matches!(
145 inf.name,
146 servers::Name::PubhubsCentral | servers::Name::Transcryptor
147 )
148 {
149 log::error!(
150 "master_enc_key_part must be set by the transcryptor and pubhub central, but no other servers - url: {source}"
151 );
152 return Err(api::ErrorCode::InternalError);
153 }
154
155 if let Some(ref ic) = inf.constellation_or_id
156 && let Some(sc) = self.constellation
157 && *ic.id() != sc.id
158 {
159 log::error!(
160 "{} at {} has a different view of the constellation of PubHubs servers",
161 inf.name,
162 source,
163 );
164 return Err(api::ErrorCode::InternalError);
165 }
166
167 api::Result::Ok(inf)
168 }
169}