tauri_plugin_http_ext/
lib.rs1mod error;
2
3use std::collections::HashMap;
4
5use http::Method;
6use reqwest::{header::HeaderMap, Certificate, Client, Identity};
7use serde::{Deserialize, Serialize};
8use serde_json::Value as JsonValue;
9use serde_repr::Deserialize_repr;
10use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
11use tauri::{Manager, Runtime, State as TauriState, async_runtime::RwLock};
12
13use error::Result;
14
15#[tauri::command]
16async fn send(state: TauriState<'_, State>, client_name: String, request: Request) -> Result<Response> {
17 let state = state.read().await;
18 let client = state.get(&client_name).unwrap();
19
20 let method = Method::from_bytes(request.method.to_uppercase().as_bytes())?;
21
22 let mut builder = client.request(method, &request.url);
23
24 if let Some(query) = request.query {
25 builder = builder.query(&query);
26 }
27
28 if let Some(headers) = request.headers {
29 builder = builder.headers(HeaderMap::try_from(&headers)?);
30 }
31
32 if let Some(body) = request.body {
33 builder = match body {
34 Body::Text(text) => builder.body(text),
35 Body::Json(json) => builder.json(&json),
36 Body::Form => builder,
37 }
38 }
39
40 let result = builder.send().await?;
41 let response = async {
42 let status = result.status().as_u16();
43
44 let mut headers: HashMap<String, String> = HashMap::new();
45 for (key, value) in result.headers() {
46 headers.insert(key.to_string(), String::from_utf8(value.as_bytes().to_vec())?);
47 }
48
49 let body: JsonValue = match request.response_type.unwrap_or(ResponseType::Json) {
50 ResponseType::Text => JsonValue::String(result.text().await?),
51 ResponseType::Json => result.json().await?,
52 ResponseType::Binary => serde_json::to_value(&*result.bytes().await?)?,
53 };
54
55 Ok(Response {
56 status,
57 headers,
58 body,
59 })
60 };
61
62 Ok((response.await as Result<Response>)?)
63}
64
65pub type Clients = HashMap<String, Client>;
66pub type State = RwLock<Clients>;
67
68pub struct Builder {
69 clients: HashMap<String, Client>,
70}
71
72impl Builder {
73 pub fn new() -> Self {
74 Self {
75 clients: HashMap::new(),
76 }
77 }
78
79 pub fn add_client(mut self, name: &str, config: ClientConfig) -> Self {
80 let mut client = Client::builder();
81
82 if let Some(tls) = config.tls {
83 client = client
84 .use_rustls_tls()
86 .add_root_certificate(Certificate::from_pem(tls.cert).unwrap());
87
88 if let Some(key) = tls.key {
89 client = client.identity(Identity::from_pem(key).unwrap());
90 }
91 }
92
93 self.clients.insert(name.to_string(), client.build().unwrap());
94 self
95 }
96
97 pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
98 let plugin = PluginBuilder::new("mtls")
99 .invoke_handler(tauri::generate_handler![send])
100 .setup(
101 move |app| {
102 app.manage(RwLock::new(self.clients.clone())); Ok(())
103 }
104 )
105 .build();
106
107 plugin
108 }
109}
110
111#[derive(Debug)]
112pub struct ClientConfig {
113 pub tls: Option<ClientTlsConfig>,
114}
115
116#[derive(Debug)]
117pub struct ClientTlsConfig {
118 pub cert: &'static [u8],
119 pub key: Option<&'static [u8]>,
120}
121
122impl ClientConfig {
123 pub fn new() -> Self {
124 Self {
125 tls: None,
126 }
127 }
128
129 pub fn tls(mut self, cert: &'static [u8], key: Option<&'static [u8]>) -> Self {
130 let tls = ClientTlsConfig {
131 cert,
132 key,
133 };
134
135 self.tls = Some(tls);
136 self
137 }
138}
139
140#[derive(Debug, Deserialize)]
141#[serde(rename_all = "camelCase")]
142struct Request {
143 pub method: String,
144 pub url: String,
145 pub query: Option<HashMap<String, String>>,
146 pub headers: Option<HashMap<String, String>>,
147 pub body: Option<Body>,
148 pub response_type: Option<ResponseType>,
149}
150
151#[derive(Debug, Serialize)]
152struct Response {
153 pub status: u16,
154 pub headers: HashMap<String, String>,
155 pub body: JsonValue,
156}
157
158#[derive(Debug, Deserialize)]
159#[serde(tag = "type", content = "payload")]
160enum Body {
161 Form,
162 Json(JsonValue),
163 Text(String),
164}
165
166#[derive(Debug, Deserialize_repr)]
167#[repr(u16)]
168enum ResponseType {
169 Json = 1,
170 Text,
171 Binary,
172}