fix 3
This commit is contained in:
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use tauri::State;
|
||||
use tokio::sync::{Mutex, oneshot};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct HttpResponse {
|
||||
@@ -9,6 +10,15 @@ struct HttpResponse {
|
||||
headers: HashMap<String, String>,
|
||||
body: String,
|
||||
time_elapsed: u128, // milliseconds
|
||||
headers_size: usize,
|
||||
body_size: usize,
|
||||
total_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct AppError {
|
||||
code: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -42,19 +52,84 @@ struct AuthConfig {
|
||||
|
||||
struct AppState {
|
||||
client: reqwest::Client,
|
||||
pending_requests: Mutex<HashMap<String, oneshot::Sender<()>>>,
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
fn new(code: &str, message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
code: code.to_string(),
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_reqwest_error(error: reqwest::Error) -> AppError {
|
||||
if error.is_timeout() {
|
||||
return AppError::new("timeout", "The request timed out after 30 seconds.");
|
||||
}
|
||||
|
||||
if error.is_connect() {
|
||||
return AppError::new("connect", format!("Failed to connect: {error}"));
|
||||
}
|
||||
|
||||
if error.is_decode() {
|
||||
return AppError::new("decode", format!("Failed to decode response body: {error}"));
|
||||
}
|
||||
|
||||
if error.is_request() {
|
||||
return AppError::new("request", format!("Failed to build or send the request: {error}"));
|
||||
}
|
||||
|
||||
AppError::new("network", format!("Request failed: {error}"))
|
||||
}
|
||||
|
||||
async fn perform_request(
|
||||
request_builder: reqwest::RequestBuilder,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let start_time = Instant::now();
|
||||
let response = request_builder.send().await.map_err(map_reqwest_error)?;
|
||||
let time_elapsed = start_time.elapsed().as_millis();
|
||||
|
||||
let status = response.status().as_u16();
|
||||
|
||||
let mut response_headers = HashMap::new();
|
||||
let mut headers_size = 2usize;
|
||||
for (key, value) in response.headers() {
|
||||
let val_bytes = value.as_bytes();
|
||||
let val_str = value.to_str().unwrap_or("").to_string();
|
||||
headers_size += key.as_str().len() + 2 + val_bytes.len() + 2;
|
||||
response_headers.insert(key.to_string(), val_str);
|
||||
}
|
||||
|
||||
let body_text = response.text().await.map_err(map_reqwest_error)?;
|
||||
let body_size = body_text.as_bytes().len();
|
||||
let total_size = headers_size + body_size;
|
||||
|
||||
Ok(HttpResponse {
|
||||
status,
|
||||
headers: response_headers,
|
||||
body: body_text,
|
||||
time_elapsed,
|
||||
headers_size,
|
||||
body_size,
|
||||
total_size,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn execute_request(
|
||||
state: State<'_, AppState>,
|
||||
request_id: String,
|
||||
method: String,
|
||||
url: String,
|
||||
headers: HashMap<String, String>,
|
||||
body: Option<String>,
|
||||
query_params: Option<HashMap<String, String>>,
|
||||
auth: Option<AuthConfig>,
|
||||
) -> Result<HttpResponse, String> {
|
||||
let req_method = method.parse::<reqwest::Method>().map_err(|e| e.to_string())?;
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let req_method = method.parse::<reqwest::Method>()
|
||||
.map_err(|e| AppError::new("invalid_method", format!("Invalid HTTP method: {e}")))?;
|
||||
let mut request_builder = state.client.request(req_method, &url);
|
||||
|
||||
// Add Query Params
|
||||
@@ -99,29 +174,40 @@ async fn execute_request(
|
||||
}
|
||||
}
|
||||
|
||||
let start_time = Instant::now();
|
||||
// Execute request
|
||||
let response = request_builder.send().await.map_err(|e| e.to_string())?;
|
||||
let time_elapsed = start_time.elapsed().as_millis();
|
||||
|
||||
let status = response.status().as_u16();
|
||||
|
||||
let mut response_headers = HashMap::new();
|
||||
for (key, value) in response.headers() {
|
||||
// Handle header value to string conversion (skipping non-utf8 for simplicity or lossy conv)
|
||||
let val_str = value.to_str().unwrap_or("").to_string();
|
||||
// Capitalize or keep standard key format? keeping standard.
|
||||
response_headers.insert(key.to_string(), val_str);
|
||||
let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>();
|
||||
{
|
||||
let mut pending_requests = state.pending_requests.lock().await;
|
||||
pending_requests.insert(request_id.clone(), cancel_tx);
|
||||
}
|
||||
|
||||
let body_text = response.text().await.map_err(|e| e.to_string())?;
|
||||
let result = tokio::select! {
|
||||
_ = &mut cancel_rx => Err(AppError::new("canceled", "The request was canceled.")),
|
||||
result = perform_request(request_builder) => result,
|
||||
};
|
||||
|
||||
Ok(HttpResponse {
|
||||
status,
|
||||
headers: response_headers,
|
||||
body: body_text,
|
||||
time_elapsed,
|
||||
})
|
||||
let mut pending_requests = state.pending_requests.lock().await;
|
||||
pending_requests.remove(&request_id);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cancel_request(
|
||||
state: State<'_, AppState>,
|
||||
request_id: String,
|
||||
) -> Result<bool, AppError> {
|
||||
let sender = {
|
||||
let mut pending_requests = state.pending_requests.lock().await;
|
||||
pending_requests.remove(&request_id)
|
||||
};
|
||||
|
||||
match sender {
|
||||
Some(cancel_tx) => {
|
||||
let _ = cancel_tx.send(());
|
||||
Ok(true)
|
||||
}
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
@@ -132,9 +218,12 @@ pub fn run() {
|
||||
.expect("failed to create HTTP client");
|
||||
|
||||
tauri::Builder::default()
|
||||
.manage(AppState { client })
|
||||
.manage(AppState {
|
||||
client,
|
||||
pending_requests: Mutex::new(HashMap::new()),
|
||||
})
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![execute_request])
|
||||
.invoke_handler(tauri::generate_handler![execute_request, cancel_request])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user