noosphere_ns/server/
client.rs1use crate::server::routes::Route;
2use crate::{dht_client::DhtClient, Multiaddr, NetworkInfo, Peer, PeerId};
3use anyhow::{anyhow, Result};
4use async_trait::async_trait;
5use noosphere_core::data::{Did, LinkRecord};
6use reqwest::Body;
7use url::Url;
8
9pub struct HttpClient {
10 api_base: Url,
11 client: reqwest::Client,
12 peer_id: PeerId,
13}
14
15impl HttpClient {
16 pub async fn new(api_base: Url) -> Result<Self> {
17 let client = reqwest::Client::new();
18 let mut url = api_base.clone();
19 url.set_path(&Route::GetPeerId.to_string());
20 let peer_id = client.get(url).send().await?.json().await?;
21
22 Ok(HttpClient {
23 api_base,
24 client,
25 peer_id,
26 })
27 }
28}
29
30#[async_trait]
31impl DhtClient for HttpClient {
32 async fn network_info(&self) -> Result<NetworkInfo> {
34 let mut url = self.api_base.clone();
35 url.set_path(&Route::NetworkInfo.to_string());
36 Ok(self.client.get(url).send().await?.json().await?)
37 }
38
39 fn peer_id(&self) -> &PeerId {
41 &self.peer_id
42 }
43
44 async fn peers(&self) -> Result<Vec<Peer>> {
46 let mut url = self.api_base.clone();
47 url.set_path(&Route::GetPeers.to_string());
48 Ok(self.client.get(url).send().await?.json().await?)
49 }
50
51 async fn add_peers(&self, peers: Vec<Multiaddr>) -> Result<()> {
54 if peers.len() > 1 {
55 return Err(anyhow!("Only one peer may be added at a time over HTTP."));
56 }
57 let address = peers.first().unwrap();
58 let mut url = self.api_base.clone();
59 let path = Route::AddPeers
60 .to_string()
61 .replace("*addr", &address.to_string());
62 url.set_path(&path);
63 Ok(self.client.post(url).send().await?.json().await?)
64 }
65
66 async fn listen(&self, listening_address: Multiaddr) -> Result<Multiaddr> {
68 let mut url = self.api_base.clone();
69 let path = Route::Listen
70 .to_string()
71 .replace("*addr", &listening_address.to_string());
72 url.set_path(&path);
73 Ok(self.client.post(url).send().await?.json().await?)
74 }
75
76 async fn stop_listening(&self) -> Result<()> {
78 let mut url = self.api_base.clone();
79 let path = Route::StopListening.to_string();
80 url.set_path(&path);
81 Ok(self.client.delete(url).send().await?.json().await?)
82 }
83
84 async fn bootstrap(&self) -> Result<()> {
86 let mut url = self.api_base.clone();
87 url.set_path(&Route::Bootstrap.to_string());
88 Ok(self.client.post(url).send().await?.json().await?)
89 }
90
91 async fn address(&self) -> Result<Option<Multiaddr>> {
93 let mut url = self.api_base.clone();
94 url.set_path(&Route::Address.to_string());
95 Ok(self.client.get(url).send().await?.json().await?)
96 }
97
98 async fn get_record(&self, identity: &Did) -> Result<Option<LinkRecord>> {
99 let mut url = self.api_base.clone();
100 let path = Route::GetRecord
101 .to_string()
102 .replace(":identity", identity.into());
103 url.set_path(&path);
104 Ok(self.client.get(url).send().await?.json().await?)
105 }
106
107 async fn put_record(&self, record: LinkRecord, quorum: usize) -> Result<()> {
108 let mut url = self.api_base.clone();
109 url.set_path(&Route::PostRecord.to_string());
110 url.set_query(Some(&format!("quorum={quorum}")));
111 let json_data = serde_json::to_string(&record)?;
112
113 let res = self
114 .client
115 .post(url)
116 .header("Content-Type", "application/json")
117 .body(Body::from(json_data))
118 .send()
119 .await?
120 .json()
121 .await?;
122 Ok(res)
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use super::*;
129 use crate::dht_client_tests;
130 use crate::{server::ApiServer, utils::wait_for_peers};
131 use crate::{NameSystem, NameSystemBuilder};
132 use noosphere_core::authority::generate_ed25519_key;
133 use noosphere_storage::{MemoryStorage, SphereDb};
134 use std::net::TcpListener;
135 use std::sync::Arc;
136 use tokio::sync::Mutex;
137
138 struct DataPlaceholder {
142 _server: ApiServer,
143 _bootstrap: NameSystem,
144 }
145
146 async fn before_each() -> Result<(DataPlaceholder, Arc<Mutex<HttpClient>>)> {
147 let (bootstrap, bootstrap_address) = {
148 let key_material = generate_ed25519_key();
149 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
150 let ns = NameSystemBuilder::default()
151 .ucan_store(store)
152 .key_material(&key_material)
153 .listening_port(0)
154 .use_test_config()
155 .build()
156 .await
157 .unwrap();
158 ns.bootstrap().await.unwrap();
159 let address = ns.address().await?.unwrap();
160 (ns, vec![address])
161 };
162
163 let api_listener = TcpListener::bind("127.0.0.1:0")?;
164 let api_port = api_listener.local_addr().unwrap().port();
165 let api_url = Url::parse(&format!("http://127.0.0.1:{}", api_port))?;
166 let key_material = generate_ed25519_key();
167 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
168
169 let ns = NameSystemBuilder::default()
170 .ucan_store(store)
171 .key_material(&key_material)
172 .bootstrap_peers(&bootstrap_address)
173 .use_test_config()
174 .build()
175 .await
176 .unwrap();
177
178 let ns = Arc::new(ns);
179 let server = ApiServer::serve(ns, api_listener);
180 let data = DataPlaceholder {
181 _server: server,
182 _bootstrap: bootstrap,
183 };
184
185 let client = {
186 let client = HttpClient::new(api_url).await?;
187 client.bootstrap().await?;
189 wait_for_peers::<HttpClient>(&client, 1).await?;
190 Arc::new(Mutex::new(client))
191 };
192
193 Ok((data, client))
194 }
195
196 dht_client_tests!(HttpClient, before_each, DataPlaceholder);
197}