use std::collections::HashMap;
use std::mem;
use std::sync::Arc;
use aws_smithy_http_server::body::{to_boxed, BoxBody};
use http::{response::Parts, Response};
use pyo3::{exceptions::PyRuntimeError, prelude::*};
use tokio::sync::Mutex;
use super::{PyHeaderMap, PyMiddlewareError};
#[pyclass(name = "Response")]
#[pyo3(text_signature = "($self, status, headers=None, body=None)")]
pub struct PyResponse {
parts: Option<Parts>,
headers: PyHeaderMap,
body: Arc<Mutex<Option<BoxBody>>>,
}
impl PyResponse {
pub fn new(response: Response<BoxBody>) -> Self {
let (mut parts, body) = response.into_parts();
let headers = mem::take(&mut parts.headers);
Self {
parts: Some(parts),
headers: PyHeaderMap::new(headers),
body: Arc::new(Mutex::new(Some(body))),
}
}
pub fn take_inner(&mut self) -> Option<Response<BoxBody>> {
let headers = self.headers.take_inner()?;
let mut parts = self.parts.take()?;
parts.headers = headers;
let body = {
let body = mem::take(&mut self.body);
let body = Arc::try_unwrap(body).ok()?;
body.into_inner().take()?
};
Some(Response::from_parts(parts, body))
}
}
#[pymethods]
impl PyResponse {
#[new]
fn newpy(
status: u16,
headers: Option<HashMap<String, String>>,
body: Option<Vec<u8>>,
) -> PyResult<Self> {
let mut builder = Response::builder().status(status);
if let Some(headers) = headers {
for (k, v) in headers {
builder = builder.header(k, v);
}
}
let response = builder
.body(body.map(to_boxed).unwrap_or_default())
.map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
Ok(Self::new(response))
}
#[getter]
fn status(&self) -> PyResult<u16> {
self.parts
.as_ref()
.map(|parts| parts.status.as_u16())
.ok_or_else(|| PyMiddlewareError::ResponseGone.into())
}
#[getter]
fn version(&self) -> PyResult<String> {
self.parts
.as_ref()
.map(|parts| format!("{:?}", parts.version))
.ok_or_else(|| PyMiddlewareError::ResponseGone.into())
}
#[getter]
fn headers(&self) -> PyHeaderMap {
self.headers.clone()
}
#[getter]
fn body<'p>(&self, py: Python<'p>) -> PyResult<&'p PyAny> {
let body = self.body.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let body = {
let mut body_guard = body.lock().await;
let body = body_guard.take().ok_or(PyMiddlewareError::RequestGone)?;
let body = hyper::body::to_bytes(body)
.await
.map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
let buf = body.clone();
body_guard.replace(to_boxed(body));
buf
};
Ok(body.to_vec())
})
}
#[setter]
fn set_body(&mut self, buf: &[u8]) {
self.body = Arc::new(Mutex::new(Some(to_boxed(buf.to_owned()))));
}
}