Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified lv_src/HTTP Client Plugin/GET.vi
Binary file not shown.
Binary file modified lv_src/HTTP Client Plugin/HTTP Client reqwest Tree.vi
Binary file not shown.
20 changes: 12 additions & 8 deletions lv_src/HTTP Client Plugin/HTTP Client reqwest.lvclass

Large diffs are not rendered by default.

74 changes: 59 additions & 15 deletions lv_src/Request/Request.lvclass

Large diffs are not rendered by default.

Binary file modified lv_src/Request/cancel.vi
Binary file not shown.
Binary file modified lv_src/Request/destroy.vi
Binary file not shown.
Binary file added lv_src/Request/get_error_kind.vi
Binary file not shown.
Binary file added lv_src/Request/get_error_message.vi
Binary file not shown.
Binary file added lv_src/Request/get_error_source.vi
Binary file not shown.
Binary file added lv_src/Request/get_error_url.vi
Binary file not shown.
Binary file added lv_src/Request/get_has_error.vi
Binary file not shown.
Binary file removed lv_src/Request/get_has_transport_error.vi
Binary file not shown.
Binary file modified lv_src/Request/get_is_complete.vi
Binary file not shown.
Binary file modified lv_src/Request/get_progress.vi
Binary file not shown.
Binary file modified lv_src/Request/get_received_bytes.vi
Binary file not shown.
Binary file modified lv_src/Request/get_response_body.vi
Binary file not shown.
Binary file modified lv_src/Request/get_response_headers.vi
Binary file not shown.
Binary file modified lv_src/Request/get_response_status.vi
Binary file not shown.
Binary file modified lv_src/Request/get_response_version.vi
Binary file not shown.
Binary file modified lv_src/Request/get_total_bytes.vi
Binary file not shown.
Binary file removed lv_src/Request/get_transport_error.vi
Binary file not shown.
Binary file modified lv_src/Request/is_valid.vi
Binary file not shown.
Binary file added lv_src/Request/raise_any_errors.vi
Binary file not shown.
Binary file modified lv_src/Request/request_id.ctl
Binary file not shown.
Binary file added lv_src/Request/reqwest_error_kind_enum.ctl
Binary file not shown.
Binary file modified lv_src/Request/set_ref.vi
Binary file not shown.
Binary file modified lv_src/lv_reqwest_32.dll
Binary file not shown.
Binary file modified lv_src/lv_reqwest_64.dll
Binary file not shown.
Binary file modified lv_src/lv_reqwest_64.so
Binary file not shown.
4 changes: 3 additions & 1 deletion lv_src/reqwest.lvproj
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@
<Item Name="CFURLCopyFileSystemPath.vi" Type="VI" URL="/&lt;vilib&gt;/Platform/CFURL.llb/CFURLCopyFileSystemPath.vi"/>
<Item Name="CFReleaseURL.vi" Type="VI" URL="/&lt;vilib&gt;/Platform/CFURL.llb/CFReleaseURL.vi"/>
<Item Name="CFStringGetCString.vi" Type="VI" URL="/&lt;vilib&gt;/Platform/CFString.llb/CFStringGetCString.vi"/>
<Item Name="Trim Whitespace One-Sided.vi" Type="VI" URL="/&lt;vilib&gt;/Utility/error.llb/Trim Whitespace One-Sided.vi"/>
<Item Name="System Exec.vi" Type="VI" URL="/&lt;vilib&gt;/Platform/system.llb/System Exec.vi"/>
</Item>
<Item Name="lv_reqwest_64.dll" Type="Document" URL="../lv_reqwest_64.dll"/>
<Item Name="lv_reqwest_64.so" Type="Document" URL="../lv_reqwest_64.so"/>
</Item>
<Item Name="Build Specifications" Type="Build"/>
</Item>
Expand Down
Binary file modified lv_src/test_client.vi
Binary file not shown.
73 changes: 69 additions & 4 deletions src/async_support.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,54 @@
use crate::types::{RequestProgress, RequestStatus, Response};
use crate::types::{
ERROR_KIND_BODY, ERROR_KIND_BUILDER, ERROR_KIND_CONNECTION, ERROR_KIND_DECODE,
ERROR_KIND_FILE_SYSTEM, ERROR_KIND_INVALID_STATUS, ERROR_KIND_NONE, ERROR_KIND_REDIRECT,
ERROR_KIND_REQUEST, ERROR_KIND_TIMEOUT, ERROR_KIND_UNKNOWN, RequestProgress, RequestStatus,
Response,
};
use reqwest::Response as ReqwestResponse;
use std::{
error::Error as StdError,
fs::File,
future::Future,
io::Write,
sync::{Arc, RwLock},
};

fn analyze_reqwest_error(e: &reqwest::Error) -> (u8, String, Option<String>, Option<String>) {
let error_kind = if e.is_timeout() {
ERROR_KIND_TIMEOUT
} else if e.is_connect() {
ERROR_KIND_CONNECTION
} else if e.is_redirect() {
ERROR_KIND_REDIRECT
} else if e.is_status() {
ERROR_KIND_INVALID_STATUS
} else if e.is_body() {
ERROR_KIND_BODY
} else if e.is_decode() {
ERROR_KIND_DECODE
} else if e.is_builder() {
ERROR_KIND_BUILDER
} else if e.is_request() {
ERROR_KIND_REQUEST
} else {
ERROR_KIND_UNKNOWN
};

let main_message = e.to_string();

let url = e.url().map(|u| u.to_string());

let source = e.source().map(|s| s.to_string());

let detailed_message = if let Some(ref src) = source {
format!("{}: {}", main_message, src)
} else {
main_message
};

(error_kind, detailed_message, url, source)
}

// Process a request and handle the response stream within an async context
pub async fn process_request(
request_future: impl Future<Output = reqwest::Result<ReqwestResponse>>,
Expand Down Expand Up @@ -56,6 +98,9 @@ pub async fn process_request(
version,
headers,
body: Err(format!("File write error: {e}")),
error_kind: ERROR_KIND_FILE_SYSTEM,
error_url: None,
error_source: Some(e.to_string()),
});
return;
}
Expand All @@ -74,6 +119,9 @@ pub async fn process_request(
version,
headers,
body: Err(format!("Network error: {e}")),
error_kind: ERROR_KIND_CONNECTION,
error_url: None,
error_source: Some(e.to_string()),
});
return;
}
Expand All @@ -88,6 +136,9 @@ pub async fn process_request(
version,
headers,
body: Ok(Vec::new()), // Empty body since it was streamed to file
error_kind: ERROR_KIND_NONE,
error_url: None,
error_source: None,
});
}
Err(e) => {
Expand All @@ -99,6 +150,9 @@ pub async fn process_request(
version,
headers,
body: Err(format!("File open error: {e}")),
error_kind: ERROR_KIND_FILE_SYSTEM,
error_url: None,
error_source: Some(e.to_string()),
});
}
}
Expand All @@ -119,6 +173,9 @@ pub async fn process_request(
version,
headers,
body: Ok(bytes_vec),
error_kind: ERROR_KIND_NONE,
error_url: None,
error_source: None,
});
}
Err(e) => {
Expand All @@ -130,20 +187,28 @@ pub async fn process_request(
version,
headers,
body: Err(format!("Body read error: {e}")),
error_kind: ERROR_KIND_BODY,
error_url: None,
error_source: Some(e.to_string()),
});
}
}
}
}
Err(e) => {
// Request error (connection failed, etc.)
let (error_kind, error_message, error_url, error_source) = analyze_reqwest_error(&e);

let mut progress = progress_info.write().unwrap();
progress.status = RequestStatus::Error;
progress.final_response = Some(Response {
status: reqwest::StatusCode::BAD_REQUEST, // Default status code for errors
version: reqwest::Version::HTTP_11, // Default to HTTP/1.1 for errors
status: e.status().unwrap_or(reqwest::StatusCode::BAD_REQUEST),
version: reqwest::Version::HTTP_11, // Default to HTTP/1.1 for errors
headers: reqwest::header::HeaderMap::new(),
body: Err(format!("Request error: {e}")),
body: Err(error_message),
error_kind,
error_url,
error_source,
});
}
}
Expand Down
222 changes: 157 additions & 65 deletions src/ffi/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,55 +120,6 @@ pub extern "C" fn request_read_response_body(
ptr::null_mut() // Return null if request not found or no response yet
}

/// Get the error message as a C string directly from a request ID
#[unsafe(no_mangle)]
pub extern "C" fn request_read_transport_error(
request_id: RequestId,
num_bytes: *mut u32,
) -> *mut c_char {
if num_bytes.is_null() {
return ptr::null_mut();
}

// Get the response info directly from the tracker
let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
// If there's no error in the response, return null
let error_str = match &response.body {
Err(error_str) => error_str,
Ok(_) => {
unsafe { *num_bytes = 0 };
return ptr::null_mut();
}
};

// Calculate size including null terminator
let str_len = error_str.len();
unsafe { *num_bytes = str_len as u32 };

// Allocate memory for the string + null terminator
let c_str_ptr = unsafe { libc::malloc(str_len + 1) as *mut c_char };
if c_str_ptr.is_null() {
return ptr::null_mut();
}

// Copy the string and add null terminator
unsafe {
std::ptr::copy_nonoverlapping(error_str.as_ptr(), c_str_ptr as *mut u8, str_len);
*(c_str_ptr.add(str_len)) = 0;
}

return c_str_ptr;
}
}

unsafe { *num_bytes = 0 };
ptr::null_mut() // Return null if request not found or no response yet
}

/// Cancel a request
#[unsafe(no_mangle)]
pub extern "C" fn request_cancel(request_id: RequestId) -> bool {
Expand Down Expand Up @@ -231,22 +182,6 @@ pub extern "C" fn request_read_response_headers(request_id: RequestId) -> *mut H
ptr::null_mut() // Return null if request not found or no response yet
}

/// Check if a request has an error (which can then be read with request_read_transport_error)
#[unsafe(no_mangle)]
pub extern "C" fn request_has_transport_error(request_id: RequestId) -> bool {
// Get the response info
let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
return response.body.is_err();
}
}

false // Return false if request not found or no response yet
}

/// Get the HTTP version as a string directly from a request ID
/// Returns a C string like "HTTP/1.1", "HTTP/2", etc. Caller must free the memory.
#[unsafe(no_mangle)]
Expand Down Expand Up @@ -296,3 +231,160 @@ pub extern "C" fn request_read_response_version(
unsafe { *num_bytes = 0 };
ptr::null_mut() // Return null if request not found or no response yet
}

/// Get the error kind from a response
#[unsafe(no_mangle)]
pub extern "C" fn request_read_error_kind(request_id: RequestId) -> u8 {
let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
return response.error_kind;
}
}

crate::types::ERROR_KIND_NONE
}

/// Get the error message with detailed information
#[unsafe(no_mangle)]
pub extern "C" fn request_read_error_message(
request_id: RequestId,
num_bytes: *mut u32,
) -> *mut c_char {
if num_bytes.is_null() {
return ptr::null_mut();
}

let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
if let Err(ref error_msg) = response.body {
let msg_len = error_msg.len();
unsafe { *num_bytes = msg_len as u32 };

// Allocate memory for the string + null terminator
let c_str_ptr = unsafe { libc::malloc(msg_len + 1) as *mut c_char };
if c_str_ptr.is_null() {
unsafe { *num_bytes = 0 };
return ptr::null_mut();
}

// Copy the string and add null terminator
unsafe {
std::ptr::copy_nonoverlapping(
error_msg.as_ptr(),
c_str_ptr as *mut u8,
msg_len,
);
*(c_str_ptr.add(msg_len)) = 0;
}

return c_str_ptr;
}
}
}

unsafe { *num_bytes = 0 };
ptr::null_mut()
}

/// Get the error URL if available
#[unsafe(no_mangle)]
pub extern "C" fn request_read_error_url(
request_id: RequestId,
num_bytes: *mut u32,
) -> *mut c_char {
if num_bytes.is_null() {
return ptr::null_mut();
}

let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
if let Some(ref url) = response.error_url {
let url_len = url.len();
unsafe { *num_bytes = url_len as u32 };

// Allocate memory for the string + null terminator
let c_str_ptr = unsafe { libc::malloc(url_len + 1) as *mut c_char };
if c_str_ptr.is_null() {
unsafe { *num_bytes = 0 };
return ptr::null_mut();
}

// Copy the string and add null terminator
unsafe {
std::ptr::copy_nonoverlapping(url.as_ptr(), c_str_ptr as *mut u8, url_len);
*(c_str_ptr.add(url_len)) = 0;
}

return c_str_ptr;
}
}
}

unsafe { *num_bytes = 0 };
ptr::null_mut()
}

/// Get the root cause error message
#[unsafe(no_mangle)]
pub extern "C" fn request_read_error_source(
request_id: RequestId,
num_bytes: *mut u32,
) -> *mut c_char {
if num_bytes.is_null() {
return ptr::null_mut();
}

let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
if let Some(ref source) = response.error_source {
let src_len = source.len();
unsafe { *num_bytes = src_len as u32 };

// Allocate memory for the string + null terminator
let c_str_ptr = unsafe { libc::malloc(src_len + 1) as *mut c_char };
if c_str_ptr.is_null() {
unsafe { *num_bytes = 0 };
return ptr::null_mut();
}

// Copy the string and add null terminator
unsafe {
std::ptr::copy_nonoverlapping(source.as_ptr(), c_str_ptr as *mut u8, src_len);
*(c_str_ptr.add(src_len)) = 0;
}

return c_str_ptr;
}
}
}

unsafe { *num_bytes = 0 };
ptr::null_mut()
}

/// Check if response has an error
#[unsafe(no_mangle)]
pub extern "C" fn request_has_error(request_id: RequestId) -> bool {
let tracker = REQUEST_TRACKER.lock().unwrap();

if let Some(progress_info) = tracker.get(&request_id) {
let progress = progress_info.read().unwrap();
if let Some(ref response) = progress.final_response {
return response.body.is_err() || response.error_kind != crate::types::ERROR_KIND_NONE;
}
}

false
}
Loading
Loading