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