use fastly_sys::fastly_http_req::{PendingResponseKind, SendErrorDetail};
use super::{PendingRequest, Request};
use crate::abi;
use crate::convert::{Borrowable, ToHeaderName, ToHeaderValue};
use crate::handle::{BodyHandle, ResponseHandle};
use crate::http::request::SendErrorCause;
#[derive(Debug)]
#[cfg_attr(target_env = "p1", derive(Eq, Hash, PartialEq))]
#[repr(transparent)]
pub struct PendingRequestHandle {
handle: u32,
}
impl From<PendingRequest> for PendingRequestHandle {
fn from(pr: PendingRequest) -> Self {
pr.pending.into_direct_handle().expect("pending request uses caching features that prevent it from being converted into a PendingRequestHandle")
}
}
impl PendingRequestHandle {
pub(crate) const INVALID: Self = Self {
handle: fastly_shared::INVALID_PENDING_REQUEST_HANDLE,
};
pub(crate) fn take(&mut self) -> Self {
std::mem::replace(self, Self::INVALID)
}
pub(crate) fn is_invalid(&self) -> bool {
self.handle == Self::INVALID.handle
}
#[doc(hidden)]
pub fn from_u32(handle: u32) -> Self {
Self { handle }
}
#[cfg_attr(
not(target_env = "p1"),
deprecated(
since = "0.11.6",
note = "This code will need to be updated for wasip2."
)
)]
pub fn as_u32(&self) -> u32 {
self.handle
}
pub(crate) fn as_u32_mut(&mut self) -> &mut u32 {
&mut self.handle
}
pub fn append_response_header(
&self,
name: impl ToHeaderName,
value: impl ToHeaderValue,
target: PendingResponseKind,
) {
let name = name.into_borrowable();
let value = value.into_borrowable();
let name = name.as_ref().as_str();
let value = value.as_ref().as_bytes();
unsafe {
abi::fastly_http_req::pending_req_header_append(
self.as_u32(),
name.as_ptr(),
name.len(),
value.as_ptr(),
value.len(),
target,
)
}
.result()
.expect("fastly_http_req::pending_req_header_append failed");
}
pub fn set_response_header(
&self,
name: impl ToHeaderName,
value: impl ToHeaderValue,
target: PendingResponseKind,
) {
let name = name.into_borrowable();
let value = value.into_borrowable();
let name = name.as_ref().as_str();
let value = value.as_ref().as_bytes();
unsafe {
abi::fastly_http_req::pending_req_header_insert(
self.as_u32(),
name.as_ptr(),
name.len(),
value.as_ptr(),
value.len(),
target,
)
}
.result()
.expect("fastly_http_req::pending_req_header_insert failed");
}
pub fn remove_response_header(&self, name: impl ToHeaderName, target: PendingResponseKind) {
let name = name.into_borrowable();
let name = name.as_ref().as_str();
unsafe {
abi::fastly_http_req::pending_req_header_remove(
self.as_u32(),
name.as_ptr(),
name.len(),
target,
)
}
.result()
.expect("fastly_http_req::pending_req_header_remove failed");
}
pub(crate) fn with_fastly_cache_miss_headers(self, original_req: &Request) -> Self {
use crate::http::header::fastly::*;
self.set_response_header(&X_CACHE, VAL_MISS, PendingResponseKind::Response);
self.set_response_header(&X_CACHE_HITS, VAL_0, PendingResponseKind::Response);
if !original_req.response_keeps_surrogate_headers() {
self.remove_response_header(SURROGATE_KEY, PendingResponseKind::Response);
self.remove_response_header(SURROGATE_CONTROL, PendingResponseKind::Response);
}
self
}
pub fn send_to_client(self) {
unsafe { abi::fastly_http_resp::send_downstream_pending(self.as_u32()) }
.result()
.expect("fastly_http_resp::send_downstream_pending failed")
}
pub fn poll(self) -> PollHandleResult {
let mut is_done = -1;
let mut resp_handle = ResponseHandle::INVALID;
let mut body_handle = BodyHandle::INVALID;
let mut error_detail = SendErrorDetail::uninitialized_all();
let status = unsafe {
abi::fastly_http_req::pending_req_poll_v2(
self.as_u32(),
&mut error_detail,
&mut is_done,
resp_handle.as_u32_mut(),
body_handle.as_u32_mut(),
)
};
if status.is_err() {
return PollHandleResult::Done(Err(SendErrorCause::detail_and_status(
error_detail,
Some(status),
)
.expect_err("status.is_err()")));
}
if is_done < 0 || is_done > 1 {
panic!("fastly_http_req_pending_req_poll internal error");
}
let is_done = is_done != 0;
if !is_done {
return PollHandleResult::Pending(self);
}
if is_done && (resp_handle.is_invalid() || body_handle.is_invalid()) {
panic!("fastly_http_req_pending_req_poll internal error");
} else {
PollHandleResult::Done(Ok((resp_handle, body_handle)))
}
}
pub fn wait(self) -> Result<(ResponseHandle, BodyHandle), SendErrorCause> {
let mut resp_handle = ResponseHandle::INVALID;
let mut body_handle = BodyHandle::INVALID;
let mut error_detail = SendErrorDetail::uninitialized_all();
let status = unsafe {
abi::fastly_http_req::pending_req_wait_v2(
self.as_u32(),
&mut error_detail,
resp_handle.as_u32_mut(),
body_handle.as_u32_mut(),
)
};
SendErrorCause::detail_and_status(error_detail, Some(status))?;
if resp_handle.is_invalid() || body_handle.is_invalid() {
panic!("fastly_http_req::pending_req_wait returned invalid handles");
}
Ok((resp_handle, body_handle))
}
}
pub enum PollHandleResult {
Pending(PendingRequestHandle),
Done(Result<(ResponseHandle, BodyHandle), SendErrorCause>),
}
pub fn select_handles<I>(
pending_reqs: I,
) -> (
Result<(ResponseHandle, BodyHandle), SendErrorCause>,
usize,
Vec<PendingRequestHandle>,
)
where
I: IntoIterator<Item = PendingRequestHandle>,
{
let mut prs = pending_reqs
.into_iter()
.map(|pr| pr.as_u32())
.collect::<Vec<u32>>();
if prs.is_empty() || prs.len() > fastly_shared::MAX_PENDING_REQS as usize {
panic!(
"the number of selected handles must be at least 1, and less than {}",
fastly_shared::MAX_PENDING_REQS
);
}
let mut done_index = -1;
let mut resp_handle = ResponseHandle::INVALID;
let mut body_handle = BodyHandle::INVALID;
let mut error_detail = SendErrorDetail::uninitialized_all();
let status = unsafe {
abi::fastly_http_req::pending_req_select_v2(
prs.as_ptr(),
prs.len(),
&mut error_detail,
&mut done_index,
resp_handle.as_u32_mut(),
body_handle.as_u32_mut(),
)
};
if status.is_err() || done_index < 0 {
panic!("fastly_http_req_pending_req_select internal error: {error_detail:?}");
}
let done_index = done_index
.try_into()
.expect("fastly_http_req_pending_req_select returned an invalid index");
prs.swap_remove(done_index);
let res = if resp_handle.is_invalid() || body_handle.is_invalid() {
Err(SendErrorCause::detail_and_status(error_detail, None)
.expect_err("invalid handles but valid status"))
} else {
Ok((resp_handle, body_handle))
};
(
res,
done_index,
prs.into_iter()
.map(PendingRequestHandle::from_u32)
.collect(),
)
}