1use crate::error::LavalinkResult;
2use crate::model::*;
3
4use std::sync::Arc;
5
6use ::http::{uri::InvalidUri, Method, Uri};
7use http_body_util::BodyExt;
8use hyper::{body::Buf, Request};
9use std::io::Read;
10
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "python", pyo3::pyclass)]
13pub struct Http {
14 pub authority: String,
15 pub rest_address: String,
16 pub rest_address_versionless: String,
17 pub headers: ::http::header::HeaderMap,
18 pub request_client: Arc<
19 hyper_util::client::legacy::Client<
20 crate::HttpsConnector,
21 http_body_util::Full<bytes::Bytes>,
22 >,
23 >,
24}
25
26impl Http {
27 pub async fn request<R: serde::de::DeserializeOwned, T: serde::Serialize + ?Sized, U>(
29 &self,
30 method: Method,
31 uri: U,
32 data: Option<&T>,
33 ) -> LavalinkResult<R>
34 where
35 Uri: TryFrom<U>,
36 <Uri as TryFrom<U>>::Error: Into<::http::Error>,
37 {
38 let mut request_builder = Request::builder().method(method).uri(uri);
39
40 {
41 *request_builder.headers_mut().unwrap() = self.headers.clone()
42 }
43
44 request_builder = request_builder.header(::http::header::HOST, &self.authority);
45
46 let request = if let Some(data) = data {
47 request_builder =
48 request_builder.header(::http::header::CONTENT_TYPE, "application/json");
49 request_builder.body(http_body_util::Full::from(serde_json::to_vec(data)?))?
50 } else {
51 request_builder.body(http_body_util::Full::default())?
52 };
53
54 let response = self.request_client.request(request).await?;
55
56 let raw_body = response.collect().await?.aggregate();
57 let body = serde_json::from_reader(raw_body.reader())?;
58
59 Ok(body)
60 }
61
62 pub async fn raw_request<T: serde::Serialize + ?Sized, U>(
64 &self,
65 method: Method,
66 uri: U,
67 data: Option<&T>,
68 ) -> LavalinkResult<String>
69 where
70 Uri: TryFrom<U>,
71 <Uri as TryFrom<U>>::Error: Into<::http::Error>,
72 {
73 let mut request_builder = Request::builder().method(method).uri(uri);
74
75 {
76 *request_builder.headers_mut().unwrap() = self.headers.clone()
77 }
78
79 request_builder = request_builder.header(::http::header::HOST, &self.authority);
80
81 let request = if let Some(data) = data {
82 request_builder =
83 request_builder.header(::http::header::CONTENT_TYPE, "application/json");
84 request_builder.body(http_body_util::Full::from(serde_json::to_vec(data)?))?
85 } else {
86 request_builder.body(http_body_util::Full::default())?
87 };
88
89 let response = self.request_client.request(request).await?;
90
91 let mut body = "".to_string();
92 let raw_body = response.collect().await?.aggregate();
93 raw_body.reader().read_to_string(&mut body)?;
94
95 Ok(body)
96 }
97
98 pub fn path_to_uri(&self, path: &str, with_version: bool) -> Result<Uri, InvalidUri> {
100 if with_version {
101 format!("{}{}", self.rest_address, path).try_into()
102 } else {
103 format!("{}{}", self.rest_address_versionless, path).try_into()
104 }
105 }
106
107 pub async fn delete_player(
109 &self,
110 guild_id: impl Into<GuildId>,
111 session_id: &str,
112 ) -> LavalinkResult<()> {
113 let guild_id = guild_id.into();
114
115 self.raw_request(
116 Method::DELETE,
117 self.path_to_uri(
118 &format!("/sessions/{}/players/{}", session_id, guild_id.0),
119 true,
120 )?,
121 None::<&()>,
122 )
123 .await?;
124
125 Ok(())
126 }
127
128 pub async fn update_player(
130 &self,
131 guild_id: impl Into<GuildId>,
132 session_id: &str,
133 data: &http::UpdatePlayer,
134 no_replace: bool,
135 ) -> LavalinkResult<player::Player> {
136 let guild_id = guild_id.into();
137
138 let uri = self.path_to_uri(
139 &format!(
140 "/sessions/{}/players/{}?noReplace={}",
141 session_id, guild_id.0, no_replace
142 ),
143 true,
144 )?;
145
146 let response = self
147 .request::<crate::error::RequestResult<_>, _, _>(Method::PATCH, uri, Some(data))
148 .await?
149 .to_result()?;
150
151 Ok(response)
152 }
153
154 pub async fn set_resuming_state(
156 &self,
157 session_id: &str,
158 resuming_state: &http::ResumingState,
159 ) -> LavalinkResult<http::ResumingState> {
160 let response = self
161 .request::<crate::error::RequestResult<_>, _, _>(
162 Method::PATCH,
163 self.path_to_uri(&format!("/sessions/{}", session_id), true)?,
164 Some(resuming_state),
165 )
166 .await?
167 .to_result()?;
168
169 Ok(response)
170 }
171
172 pub async fn load_tracks(&self, identifier: &str) -> LavalinkResult<track::Track> {
181 let uri = self.path_to_uri(
182 &format!("/loadtracks?identifier={}", urlencoding::encode(identifier)),
183 true,
184 )?;
185
186 let response = self
187 .request::<crate::error::RequestResult<track::Track>, _, _>(
188 Method::GET,
189 uri,
190 None::<&()>,
191 )
192 .await?
193 .to_result()?;
194
195 match response.data {
196 Some(track::TrackLoadData::Error(why)) => Err(why.into()),
197 _ => Ok(response),
198 }
199 }
200
201 pub async fn version(&self) -> LavalinkResult<String> {
203 let response = self
204 .raw_request(
205 Method::GET,
206 self.path_to_uri("/version", false)?,
207 None::<&()>,
208 )
209 .await?;
210
211 Ok(response)
212 }
213
214 pub async fn stats(&self) -> LavalinkResult<events::Stats> {
218 let response = self
219 .request::<crate::error::RequestResult<_>, _, _>(
220 Method::GET,
221 self.path_to_uri("/stats", true)?,
222 None::<&()>,
223 )
224 .await?
225 .to_result()?;
226
227 Ok(response)
228 }
229
230 pub async fn info(&self) -> LavalinkResult<http::Info> {
232 let response = self
233 .request::<crate::error::RequestResult<_>, _, _>(
234 Method::GET,
235 self.path_to_uri("/info", true)?,
236 None::<&()>,
237 )
238 .await?
239 .to_result()?;
240
241 Ok(response)
242 }
243
244 pub async fn decode_track(&self, track: &str) -> LavalinkResult<track::TrackData> {
250 let uri = self.path_to_uri(
251 &format!("/decodetrack?encodedTrack={}", urlencoding::encode(track)),
252 true,
253 )?;
254
255 let response = self
256 .request::<crate::error::RequestResult<_>, _, _>(Method::GET, uri, None::<&()>)
257 .await?
258 .to_result()?;
259
260 Ok(response)
261 }
262
263 pub async fn decode_tracks(&self, tracks: &[String]) -> LavalinkResult<Vec<track::TrackData>> {
269 let response = self
270 .request::<crate::error::RequestResult<_>, _, _>(
271 Method::POST,
272 self.path_to_uri("/decodetracks", true)?,
273 Some(tracks),
274 )
275 .await?
276 .to_result()?;
277
278 Ok(response)
279 }
280
281 pub async fn get_player(
283 &self,
284 guild_id: impl Into<GuildId>,
285 session_id: &str,
286 ) -> LavalinkResult<player::Player> {
287 let guild_id = guild_id.into();
288
289 let response = self
290 .request::<crate::error::RequestResult<_>, _, _>(
291 Method::GET,
292 self.path_to_uri(
293 &format!("/sessions/{}/players/{}", session_id, guild_id.0),
294 true,
295 )?,
296 None::<&()>,
297 )
298 .await?
299 .to_result()?;
300
301 Ok(response)
302 }
303
304 pub async fn get_players(&self, session_id: &str) -> LavalinkResult<Vec<player::Player>> {
306 let response = self
307 .request::<crate::error::RequestResult<_>, _, _>(
308 Method::GET,
309 self.path_to_uri(&format!("/sessions/{}/players", session_id), true)?,
310 None::<&()>,
311 )
312 .await?
313 .to_result()?;
314
315 Ok(response)
316 }
317}