1use std::{
2 borrow::Cow,
3 convert::Infallible,
4 pin::Pin,
5 task::{Context, Poll},
6};
7
8use bytes::Bytes;
9use http_body_util::BodyExt;
10
11use crate::headers::{self, HeaderMapExt};
12
13type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Infallible>;
14
15#[derive(Debug)]
17pub struct ResponseBody(BoxBody);
18
19impl ResponseBody {
20 pub(crate) fn empty() -> Self {
22 ResponseBody(http_body_util::Empty::new().boxed_unsync())
23 }
24
25 pub(crate) fn full(data: Bytes) -> Self {
27 ResponseBody(http_body_util::Full::new(data).boxed_unsync())
28 }
29}
30
31impl http_body::Body for ResponseBody {
32 type Data = Bytes;
33 type Error = Infallible;
34
35 fn poll_frame(
36 mut self: Pin<&mut Self>,
37 cx: &mut Context<'_>,
38 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
39 Pin::new(&mut self.0).poll_frame(cx)
40 }
41
42 fn is_end_stream(&self) -> bool {
43 self.0.is_end_stream()
44 }
45
46 fn size_hint(&self) -> http_body::SizeHint {
47 self.0.size_hint()
48 }
49}
50
51pub(crate) struct File {
53 pub content: Cow<'static, [u8]>,
54 pub content_type: headers::ContentType,
55 pub etag: headers::ETag,
56 pub last_modified: Option<headers::LastModified>,
57}
58
59pub struct ResponseFuture {
63 inner: ResponseFutureInner,
64}
65
66enum ResponseFutureInner {
67 Ready(Option<http::Response<ResponseBody>>),
68}
69
70impl ResponseFuture {
71 pub(crate) fn method_not_allowed() -> Self {
72 let response = http::Response::builder()
73 .header(
74 http::header::ALLOW,
75 http::HeaderValue::from_static("GET, HEAD"),
76 )
77 .status(http::StatusCode::METHOD_NOT_ALLOWED)
78 .body(ResponseBody::empty())
79 .unwrap();
80
81 Self {
82 inner: ResponseFutureInner::Ready(Some(response)),
83 }
84 }
85
86 pub(crate) fn file_not_found() -> Self {
87 let response = http::Response::builder()
88 .status(http::StatusCode::NOT_FOUND)
89 .body(ResponseBody::empty())
90 .unwrap();
91
92 Self {
93 inner: ResponseFutureInner::Ready(Some(response)),
94 }
95 }
96
97 pub(crate) fn not_modified() -> Self {
98 let response = http::Response::builder()
99 .status(http::StatusCode::NOT_MODIFIED)
100 .body(ResponseBody::empty())
101 .unwrap();
102
103 Self {
104 inner: ResponseFutureInner::Ready(Some(response)),
105 }
106 }
107
108 pub(crate) fn file(file: File) -> Self {
109 let mut response = http::Response::builder()
110 .status(http::StatusCode::OK)
111 .body(ResponseBody::full(match file.content {
112 Cow::Borrowed(bytes) => Bytes::from_static(bytes),
113 Cow::Owned(bytes) => Bytes::from_owner(bytes),
114 }))
115 .unwrap();
116
117 response.headers_mut().typed_insert(file.content_type);
118 response.headers_mut().typed_insert(file.etag);
119 if let Some(last_modified) = file.last_modified {
120 response.headers_mut().typed_insert(last_modified);
121 }
122
123 Self {
124 inner: ResponseFutureInner::Ready(Some(response)),
125 }
126 }
127}
128
129impl Future for ResponseFuture {
130 type Output = Result<http::Response<ResponseBody>, std::convert::Infallible>;
131
132 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
133 let response = match self.get_mut().inner {
134 ResponseFutureInner::Ready(ref mut response) => response
135 .take()
136 .expect("ResponseFuture polled after completion"),
137 };
138 Poll::Ready(Ok(response))
139 }
140}