client.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*************************************************************************
  2. *
  3. * Copyright (C) 2018-2025 Ruilin Peng (Nick) <[email protected]>.
  4. *
  5. * smartdns is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * smartdns is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. use http::uri;
  19. use reqwest;
  20. use smartdns_ui::http_api_msg;
  21. use std::{error::Error, net::TcpStream};
  22. use tungstenite::*;
  23. pub struct TestClient {
  24. url: String,
  25. token: Option<http_api_msg::TokenResponse>,
  26. client: reqwest::blocking::Client,
  27. no_auth_header: bool,
  28. }
  29. impl TestClient {
  30. pub fn new(url: &String) -> Self {
  31. let client = TestClient {
  32. url: url.clone(),
  33. token: None,
  34. client: reqwest::blocking::ClientBuilder::new()
  35. .danger_accept_invalid_certs(true)
  36. .build()
  37. .unwrap(),
  38. no_auth_header: false,
  39. };
  40. client
  41. }
  42. #[allow(dead_code)]
  43. pub fn set_with_auth_header(&mut self, with_auth_header: bool) {
  44. self.no_auth_header = with_auth_header;
  45. }
  46. #[allow(dead_code)]
  47. pub fn login(&mut self, username: &str, password: &str) -> Result<String, Box<dyn Error>> {
  48. let url = self.url.clone() + "/api/auth/login";
  49. let body = http_api_msg::api_msg_gen_auth_login(&http_api_msg::AuthUser {
  50. username: username.to_string(),
  51. password: password.to_string(),
  52. });
  53. let resp = self.client.post(&url).body(body).send()?;
  54. let text = resp.text()?;
  55. let token = http_api_msg::api_msg_parse_auth_token(&text)?;
  56. self.token = Some(token);
  57. Ok(text)
  58. }
  59. #[allow(dead_code)]
  60. pub fn logout(&mut self) -> Result<String, Box<dyn Error>> {
  61. let url = self.url.clone() + "/api/auth/logout";
  62. let resp = self.client.post(&url).send()?;
  63. let text = resp.text()?;
  64. self.token = None;
  65. Ok(text)
  66. }
  67. fn prep_request(
  68. &self,
  69. method: reqwest::Method,
  70. path: &str,
  71. ) -> Result<reqwest::blocking::RequestBuilder, Box<dyn Error>> {
  72. let url = self.url.clone() + path;
  73. let mut req = self.client.request(method, url);
  74. if let Some(token) = &self.token {
  75. if self.token.is_some() && !self.no_auth_header {
  76. req = req.header("Authorization", format!("Bearer {}", token.token));
  77. }
  78. }
  79. Ok(req)
  80. }
  81. pub fn get(&self, path: &str) -> Result<(i32, String), Box<dyn Error>> {
  82. let req = self.prep_request(reqwest::Method::GET, path)?;
  83. let resp = req.send()?;
  84. let status = resp.status().as_u16();
  85. let text = resp.text()?;
  86. Ok((status as i32, text))
  87. }
  88. #[allow(dead_code)]
  89. pub fn delete(&self, path: &str) -> Result<(i32, String), Box<dyn Error>> {
  90. let req = self.prep_request(reqwest::Method::DELETE, path)?;
  91. let resp = req.send()?;
  92. let status = resp.status().as_u16();
  93. let text = resp.text()?;
  94. Ok((status as i32, text))
  95. }
  96. #[allow(dead_code)]
  97. pub fn put(&self, path: &str, body: &str) -> Result<(i32, String), Box<dyn Error>> {
  98. let req = self.prep_request(reqwest::Method::PUT, path)?;
  99. let resp = req.body(body.to_string()).send()?;
  100. let status = resp.status().as_u16();
  101. let text = resp.text()?;
  102. Ok((status as i32, text))
  103. }
  104. #[allow(dead_code)]
  105. pub fn post(&self, path: &str, body: &str) -> Result<(i32, String), Box<dyn Error>> {
  106. let req = self.prep_request(reqwest::Method::POST, path)?;
  107. let resp = req.body(body.to_string()).send()?;
  108. let status = resp.status().as_u16();
  109. let text = resp.text()?;
  110. Ok((status as i32, text))
  111. }
  112. #[allow(dead_code)]
  113. pub fn websocket(
  114. &self,
  115. path: &str,
  116. ) -> Result<WebSocket<stream::MaybeTlsStream<TcpStream>>, Box<dyn Error>> {
  117. let url = self.url.clone() + path;
  118. let uri: http::Uri = url.parse()?;
  119. let mut parts = uri.into_parts();
  120. parts.scheme = Some("ws".parse().unwrap());
  121. let uri = uri::Uri::from_parts(parts).unwrap();
  122. let mut request_builder = tungstenite::ClientRequestBuilder::new(uri);
  123. if let Some(token) = &self.token {
  124. if self.token.is_some() {
  125. request_builder =
  126. request_builder.with_header("Authorization", format!("Bearer {}", token.token));
  127. }
  128. }
  129. request_builder = request_builder
  130. .with_header("Upgrade", "websocket")
  131. .with_header("Sec-WebSocket-Version", "13")
  132. .with_header("Connection", "keep-alive, Upgrade");
  133. let ret = tungstenite::connect(request_builder);
  134. if let Err(e) = ret {
  135. println!("websocket connect error: {:?}", e.to_string());
  136. return Err(Box::new(e));
  137. }
  138. let (socket, _) = ret.unwrap();
  139. Ok(socket)
  140. }
  141. }
  142. impl Drop for TestClient {
  143. fn drop(&mut self) {}
  144. }