A pit full of rusty nails
1//! Util for defining a customised HTTP stream response, but for text/html or other headers.
2//!
3
4use std::{
5 convert::Infallible,
6 pin::Pin,
7 task::{Context, Poll},
8};
9
10use axum::{
11 body::Body,
12 http::{HeaderValue, Response},
13 response::IntoResponse,
14};
15use futures_lite::Stream;
16use hyper::{
17 body::{Bytes, Frame},
18 header::CONTENT_TYPE,
19};
20
21const CONTENT_TYPE_VALUE: HeaderValue = HeaderValue::from_static("text/html; charset=utf-8");
22
23pub struct NailResponseStream<S> {
24 stream: S,
25}
26
27impl<S> NailResponseStream<S>
28where
29 S: Stream<Item = Bytes> + Unpin + Send + 'static,
30{
31 #[inline]
32 pub fn from_stream(stream: S) -> Self {
33 Self { stream }
34 }
35}
36
37impl<S> hyper::body::Body for NailResponseStream<S>
38where
39 S: Stream<Item = Bytes> + Unpin + Send + 'static,
40{
41 type Data = Bytes;
42 type Error = Infallible;
43
44 #[inline]
45 fn poll_frame(
46 mut self: Pin<&mut Self>,
47 cx: &mut Context<'_>,
48 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
49 match core::pin::pin!(&mut self.stream).poll_next(cx) {
50 Poll::Ready(Some(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes)))),
51 Poll::Ready(None) => Poll::Ready(None),
52 Poll::Pending => Poll::Pending,
53 }
54 }
55}
56
57impl<S> IntoResponse for NailResponseStream<S>
58where
59 S: Stream<Item = Bytes> + Unpin + Send + 'static,
60{
61 #[inline]
62 fn into_response(self) -> Response<Body> {
63 let mut response: Response<Body> = Response::new(Body::new(self));
64 response
65 .headers_mut()
66 .append(CONTENT_TYPE, CONTENT_TYPE_VALUE);
67 response
68 }
69}