use std::{ffi::c_void, sync::Arc};
use windows_sys::Win32::Networking::WinHttp::*;
use super::context::{RequestContext, RequestState};
use crate::error::WinHttpError;
pub(crate) unsafe extern "system" fn winhttp_callback(
_h_internet: *mut c_void,
dw_context: usize,
dw_internet_status: u32,
lpv_status_information: *mut c_void,
dw_status_information_length: u32,
) {
if dw_context == 0 {
return;
}
let ctx = if dw_internet_status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING {
let ctx = unsafe { Arc::<RequestContext>::from_raw(dw_context as _) };
drop(ctx);
return;
} else {
unsafe { &*(dw_context as *const RequestContext) }
};
handle_callback(
ctx,
dw_internet_status,
lpv_status_information,
dw_status_information_length,
);
}
unsafe fn handle_callback(
ctx: &RequestContext,
status: u32,
status_info: *mut c_void,
status_info_len: u32,
) {
match status {
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE => {
handle_send_complete(ctx);
}
WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE => {
handle_headers_available(ctx);
}
WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE => {
handle_data_available(ctx, status_info);
}
WINHTTP_CALLBACK_STATUS_READ_COMPLETE => {
handle_read_complete(ctx, status_info, status_info_len);
}
WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE => {
handle_write_complete(ctx, status_info);
}
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR => {
handle_request_error(ctx, status_info);
}
WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING => {
}
_ => {
}
}
}
fn handle_send_complete(ctx: &RequestContext) {
ctx.set_send_complete();
}
fn handle_headers_available(ctx: &RequestContext) {
ctx.transition_state(RequestState::HeadersReceived);
}
unsafe fn handle_data_available(ctx: &RequestContext, status_info: *mut c_void) {
if status_info.is_null() {
ctx.set_error(WinHttpError::from_code(
0,
"null status_info in DATA_AVAILABLE",
));
return;
}
let bytes_available = *(status_info as *const u32);
ctx.set_bytes_available(bytes_available);
if bytes_available == 0 {
ctx.transition_state(RequestState::Completed);
} else {
ctx.transition_state(RequestState::DataAvailable);
}
}
unsafe fn handle_read_complete(
ctx: &RequestContext,
status_info: *mut c_void,
status_info_len: u32,
) {
let bytes_read = status_info_len as usize;
ctx.set_read_complete(status_info as _, bytes_read);
}
unsafe fn handle_write_complete(ctx: &RequestContext, status_info: *mut c_void) {
let bytes_written = if status_info.is_null() {
0
} else {
unsafe { *(status_info as *const u32) as usize }
};
ctx.set_write_complete(bytes_written);
}
unsafe fn handle_request_error(ctx: &RequestContext, status_info: *mut c_void) {
if status_info.is_null() {
ctx.set_error(WinHttpError::from_code(
0,
"null status_info in REQUEST_ERROR",
));
return;
}
let result = &*(status_info as *const WINHTTP_ASYNC_RESULT);
let error_code = result.dwError;
ctx.set_error(WinHttpError::from_code(error_code, "async request error"));
}
pub(crate) fn setup_session_callback(
session: &crate::handle::SessionHandle,
) -> crate::error::Result<()> {
unsafe {
session.set_status_callback(
Some(winhttp_callback),
WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
)?;
}
Ok(())
}