use std::{fmt, io};
use bytes::Bytes;
use futures_core::Stream;
use crate::{
common::{HttpVersion, StatusCode},
headers::{HeaderName, Headers, header_keys},
};
pub type BoxBodyStream = Box<dyn Stream<Item = io::Result<Bytes>> + Send + Unpin>;
pub enum ResponseBody {
Empty,
Full(Bytes),
Stream(BoxBodyStream),
}
impl fmt::Debug for ResponseBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ResponseBody::Empty => f.write_str("ResponseBody::Empty"),
ResponseBody::Full(bytes) => f.debug_tuple("ResponseBody::Full").field(bytes).finish(),
ResponseBody::Stream(_) => f.write_str("ResponseBody::Stream(<stream>)"),
}
}
}
impl ResponseBody {
pub fn is_empty(&self) -> bool {
matches!(self, ResponseBody::Empty)
}
pub fn len(&self) -> Option<usize> {
match self {
ResponseBody::Empty => Some(0),
ResponseBody::Full(bytes) => Some(bytes.len()),
ResponseBody::Stream(_) => None,
}
}
pub fn is_stream(&self) -> bool {
matches!(self, ResponseBody::Stream(_))
}
}
#[derive(Debug)]
pub struct Response {
version: HttpVersion,
status: StatusCode,
reason: Option<String>,
headers: Headers,
body: ResponseBody,
trailers: Option<Headers>,
}
impl Response {
pub fn new(status: StatusCode) -> Self {
Self {
version: HttpVersion::HTTP_1_1,
reason: None,
status,
headers: Headers::new(),
body: ResponseBody::Empty,
trailers: None,
}
}
pub fn builder(status: StatusCode) -> ResponseBuilder {
ResponseBuilder::new(status)
}
pub fn version(&self) -> HttpVersion {
self.version
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn reason_phrase(&self) -> &str {
self.reason
.as_deref()
.or_else(|| self.status.canonical_reason())
.unwrap_or("")
}
pub fn headers(&self) -> &Headers {
&self.headers
}
pub fn headers_mut(&mut self) -> &mut Headers {
&mut self.headers
}
pub fn body(&self) -> &ResponseBody {
&self.body
}
pub fn set_status(&mut self, status: StatusCode) {
self.status = status;
}
pub fn take_body(&mut self) -> ResponseBody {
std::mem::replace(&mut self.body, ResponseBody::Empty)
}
pub fn set_body(&mut self, body: ResponseBody) {
self.body = body;
}
pub fn set_body_bytes(&mut self, bytes: impl Into<Bytes>) {
self.body = ResponseBody::Full(bytes.into());
}
pub fn set_body_static(&mut self, bytes: &'static [u8]) {
self.body = ResponseBody::Full(Bytes::from_static(bytes));
}
pub fn set_body_text_static(&mut self, text: &'static str) {
self.set_body_static(text.as_bytes());
}
pub fn strip_body_for_head(&mut self) {
if let ResponseBody::Full(bytes) = &self.body {
if !self.headers.contains(header_keys::CONTENT_LENGTH) {
self.headers
.insert(header_keys::CONTENT_LENGTH, bytes.len().to_string());
}
}
self.body = ResponseBody::Empty;
}
pub fn trailers(&self) -> Option<&Headers> {
self.trailers.as_ref()
}
pub fn set_trailers(&mut self, trailers: Headers) {
self.trailers = Some(trailers);
}
pub fn take_trailers(&mut self) -> Option<Headers> {
self.trailers.take()
}
pub fn body_mut(&mut self) -> &mut ResponseBody {
&mut self.body
}
pub fn set_version(&mut self, version: HttpVersion) {
self.version = version;
}
pub fn set_reason(&mut self, reason: impl Into<String>) {
self.reason = Some(reason.into());
}
}
#[derive(Debug)]
pub struct ResponseBuilder {
version: HttpVersion,
status: StatusCode,
reason: Option<String>,
headers: Headers,
body: ResponseBody,
trailers: Option<Headers>,
}
impl ResponseBuilder {
fn new(status: StatusCode) -> Self {
Self {
version: HttpVersion::HTTP_1_1,
status,
reason: None,
headers: Headers::new(),
body: ResponseBody::Empty,
trailers: None,
}
}
pub fn version(mut self, version: HttpVersion) -> Self {
self.version = version;
self
}
pub fn reason(mut self, reason: impl Into<String>) -> Self {
self.reason = Some(reason.into());
self
}
pub fn header(mut self, name: impl Into<HeaderName>, value: impl Into<String>) -> Self {
self.headers.insert(name, value);
self
}
pub fn body(mut self, body: ResponseBody) -> Self {
self.body = body;
self
}
pub fn body_bytes(mut self, bytes: impl Into<Bytes>) -> Self {
self.body = ResponseBody::Full(bytes.into());
self
}
pub fn body_static(mut self, bytes: &'static [u8]) -> Self {
self.body = ResponseBody::Full(Bytes::from_static(bytes));
self
}
pub fn text_static(self, text: &'static str) -> Self {
self.body_static(text.as_bytes())
}
pub fn trailers(mut self, trailers: Headers) -> Self {
self.trailers = Some(trailers);
self
}
pub fn build(self) -> Response {
Response {
version: self.version,
status: self.status,
reason: self.reason,
headers: self.headers,
body: self.body,
trailers: self.trailers,
}
}
}