use std::ffi::{OsStr, OsString};
use std::mem::MaybeUninit;
use std::os::unix::prelude::OsStrExt;
use std::ptr::NonNull;
use mountpoint_s3_crt_sys::*;
use thiserror::Error;
use crate::common::allocator::Allocator;
use crate::common::error::Error;
use crate::http::http_library_init;
use crate::io::async_stream::AsyncInputStream;
use crate::{aws_byte_cursor_as_slice, CrtError, ToAwsByteCursor};
#[derive(Debug)]
pub struct Header<N: AsRef<OsStr>, V: AsRef<OsStr>> {
inner: aws_http_header,
name: N,
value: V,
}
impl<N: AsRef<OsStr>, V: AsRef<OsStr>> Header<N, V> {
pub fn new(name: N, value: V) -> Self {
let inner = unsafe {
aws_http_header {
name: name.as_ref().as_aws_byte_cursor(),
value: value.as_ref().as_aws_byte_cursor(),
..Default::default()
}
};
Self { name, value, inner }
}
pub fn name(&self) -> &N {
&self.name
}
pub fn value(&self) -> &V {
&self.value
}
}
#[derive(Debug)]
pub struct Headers {
inner: NonNull<aws_http_headers>,
}
unsafe impl Send for Headers {}
unsafe impl Sync for Headers {}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum HeadersError {
#[error("Header not found")]
HeaderNotFound,
#[error("CRT error: {0}")]
CrtError(#[source] Error),
}
impl From<Error> for HeadersError {
fn from(err: Error) -> Self {
if err == (aws_http_errors::AWS_ERROR_HTTP_HEADER_NOT_FOUND as i32).into() {
Self::HeaderNotFound
} else {
Self::CrtError(err)
}
}
}
impl Headers {
pub(crate) unsafe fn from_crt(ptr: NonNull<aws_http_headers>) -> Self {
aws_http_headers_acquire(ptr.as_ptr());
Self { inner: ptr }
}
pub fn new(allocator: &Allocator) -> Result<Self, HeadersError> {
let inner = unsafe { aws_http_headers_new(allocator.inner.as_ptr()).ok_or_last_error()? };
Ok(Self { inner })
}
pub fn count(&self) -> usize {
unsafe { aws_http_headers_count(self.inner.as_ptr()) }
}
pub fn get_index(&self, index: usize) -> Result<Header<OsString, OsString>, HeadersError> {
let header = unsafe {
let mut header: MaybeUninit<aws_http_header> = MaybeUninit::uninit();
aws_http_headers_get_index(self.inner.as_ptr(), index, header.as_mut_ptr()).ok_or_last_error()?;
header.assume_init()
};
let (name, value) = unsafe {
(
OsStr::from_bytes(aws_byte_cursor_as_slice(&header.name)).to_owned(),
OsStr::from_bytes(aws_byte_cursor_as_slice(&header.value)).to_owned(),
)
};
Ok(Header::new(name, value))
}
pub fn add_header(&mut self, header: &Header<impl AsRef<OsStr>, impl AsRef<OsStr>>) -> Result<(), HeadersError> {
if self.has_header(header.name()) {
self.erase_header(header.name())?;
}
unsafe {
aws_http_headers_add_header(self.inner.as_ptr(), &header.inner).ok_or_last_error()?;
}
Ok(())
}
pub fn has_header(&self, name: impl AsRef<OsStr>) -> bool {
unsafe { aws_http_headers_has(self.inner.as_ptr(), name.as_ref().as_aws_byte_cursor()) }
}
pub fn erase_header(&self, name: impl AsRef<OsStr>) -> Result<(), HeadersError> {
unsafe {
aws_http_headers_erase(self.inner.as_ptr(), name.as_ref().as_aws_byte_cursor()).ok_or_last_error()?;
}
Ok(())
}
pub fn get<H: AsRef<OsStr>>(&self, name: H) -> Result<Header<OsString, OsString>, HeadersError> {
let value = unsafe {
let mut value: MaybeUninit<aws_byte_cursor> = MaybeUninit::uninit();
aws_http_headers_get(
self.inner.as_ptr(),
name.as_ref().as_aws_byte_cursor(),
value.as_mut_ptr(),
)
.ok_or_last_error()?;
value.assume_init()
};
let name = name.as_ref().to_os_string();
let value = unsafe { OsStr::from_bytes(aws_byte_cursor_as_slice(&value)).to_owned() };
Ok(Header::new(name, value))
}
pub fn iter(&self) -> impl Iterator<Item = (OsString, OsString)> + '_ {
HeadersIterator {
headers: self,
offset: 0,
}
}
}
impl Drop for Headers {
fn drop(&mut self) {
unsafe {
aws_http_headers_release(self.inner.as_ptr());
}
}
}
#[derive(Debug)]
struct HeadersIterator<'a> {
headers: &'a Headers,
offset: usize,
}
impl<'a> Iterator for HeadersIterator<'a> {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<Self::Item> {
if self.offset < self.headers.count() {
let header = self
.headers
.get_index(self.offset)
.expect("HeadersIterator: failed to get next header");
self.offset += 1;
Some((header.name, header.value))
} else {
None
}
}
}
#[derive(Debug)]
pub struct Message {
pub(crate) inner: NonNull<aws_http_message>,
body_input_stream: Option<AsyncInputStream>,
}
impl Message {
pub fn new_request(allocator: &Allocator) -> Result<Self, Error> {
http_library_init(allocator);
let inner = unsafe { aws_http_message_new_request(allocator.inner.as_ptr()).ok_or_last_error()? };
Ok(Self {
inner,
body_input_stream: None,
})
}
pub fn add_header(&mut self, header: &Header<impl AsRef<OsStr>, impl AsRef<OsStr>>) -> Result<(), Error> {
unsafe { aws_http_message_add_header(self.inner.as_ptr(), header.inner).ok_or_last_error() }
}
pub fn set_request_path(&mut self, path: impl AsRef<OsStr>) -> Result<(), Error> {
unsafe { aws_http_message_set_request_path(self.inner.as_ptr(), path.as_aws_byte_cursor()).ok_or_last_error() }
}
pub fn set_request_method(&mut self, method: impl AsRef<OsStr>) -> Result<(), Error> {
unsafe {
aws_http_message_set_request_method(self.inner.as_ptr(), method.as_aws_byte_cursor()).ok_or_last_error()
}
}
pub fn body_stream(&self) -> &Option<AsyncInputStream> {
&self.body_input_stream
}
pub fn set_body_stream(&mut self, input_stream: Option<AsyncInputStream>) -> Option<AsyncInputStream> {
std::mem::replace(&mut self.body_input_stream, input_stream)
}
pub fn get_headers(&mut self) -> Result<Headers, Error> {
let header_ptr = unsafe { aws_http_message_get_headers(self.inner.as_ptr()).ok_or_last_error()? };
let headers = unsafe { Headers::from_crt(header_ptr) };
Ok(headers)
}
}
impl Drop for Message {
fn drop(&mut self) {
unsafe {
aws_http_message_release(self.inner.as_ptr());
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::common::allocator::Allocator;
use std::collections::HashMap;
#[test]
fn test_headers() {
let mut headers = Headers::new(&Allocator::default()).expect("failed to create headers");
headers.add_header(&Header::new("a", "1")).unwrap();
headers.add_header(&Header::new("b", "2")).unwrap();
headers.add_header(&Header::new("c", "3")).unwrap();
assert_eq!(headers.count(), 3);
assert!(headers.has_header("a"));
assert!(headers.has_header("b"));
assert!(headers.has_header("c"));
assert_eq!(headers.get("a").unwrap().name(), "a");
assert_eq!(headers.get("a").unwrap().value(), "1");
let map: HashMap<OsString, OsString> = headers.iter().collect();
assert_eq!(map.len(), 3);
assert_eq!(map.get(OsStr::new("a")), Some(&OsString::from("1")));
}
#[test]
fn test_header_not_present() {
let headers = Headers::new(&Allocator::default()).expect("failed to create headers");
assert!(!headers.has_header("a"));
let error = headers.get("a").expect_err("should fail because header is not present");
assert_eq!(error, HeadersError::HeaderNotFound, "should fail with HeaderNotFound");
}
#[test]
fn test_headers_overwrite() {
let mut headers = Headers::new(&Allocator::default()).expect("failed to create headers");
headers.add_header(&Header::new("a", "1")).unwrap();
headers.add_header(&Header::new("a", "2")).unwrap();
assert_eq!(headers.count(), 1);
assert_eq!(headers.get("a").unwrap().name(), "a");
assert_eq!(headers.get("a").unwrap().value(), "2");
let map: HashMap<OsString, OsString> = headers.iter().collect();
assert_eq!(map.len(), 1);
assert_eq!(map.get(OsStr::new("a")), Some(&OsString::from("2")));
}
#[test]
fn test_headers_erase() {
let mut headers = Headers::new(&Allocator::default()).expect("failed to create headers");
headers.add_header(&Header::new("a", "1")).unwrap();
assert_eq!(headers.count(), 1);
headers.erase_header("a").unwrap();
assert_eq!(headers.count(), 0);
let map: HashMap<OsString, OsString> = headers.iter().collect();
assert_eq!(map.len(), 0);
}
}