Skip to main content

rama_http/layer/traffic_writer/
response.rs

1use super::{WriterMode, write_headers_body_flags};
2use crate::io::write_http_response;
3use crate::{Body, Request, Response, StreamingBody, body::util::BodyExt};
4use rama_core::bytes::Bytes;
5use rama_core::error::{BoxError, ErrorContext};
6use rama_core::extensions::{Extension, ExtensionsRef};
7use rama_core::rt::Executor;
8use rama_core::telemetry::tracing::{self, Instrument};
9use rama_core::{Layer, Service};
10use rama_utils::macros::define_inner_service_accessors;
11use std::fmt::Debug;
12use std::sync::Arc;
13use tokio::io::{AsyncWrite, stderr, stdout};
14use tokio::sync::mpsc::{Sender, UnboundedSender, channel, unbounded_channel};
15
16/// Layer that applies [`ResponseWriterService`] which prints the http response in std format.
17#[derive(Clone)]
18pub struct ResponseWriterLayer<W> {
19    writer: W,
20}
21
22impl<W> Debug for ResponseWriterLayer<W> {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.debug_struct("ResponseWriterLayer")
25            .field("writer", &format_args!("{}", std::any::type_name::<W>()))
26            .finish()
27    }
28}
29
30impl<W> ResponseWriterLayer<W> {
31    /// Create a new [`ResponseWriterLayer`] with a custom [`ResponseWriter`].
32    pub const fn new(writer: W) -> Self {
33        Self { writer }
34    }
35}
36
37/// A trait for writing http responses.
38pub trait ResponseWriter: Send + Sync + 'static {
39    /// Write the http response.
40    fn write_response(&self, res: Response) -> impl Future<Output = ()> + Send + '_;
41}
42
43/// Marker struct to indicate that the response should not be printed.
44#[derive(Debug, Clone, Default, Extension)]
45#[extension(tags(http))]
46#[non_exhaustive]
47pub struct DoNotWriteResponse;
48
49impl DoNotWriteResponse {
50    /// Create a new [`DoNotWriteResponse`] marker.
51    #[must_use]
52    pub const fn new() -> Self {
53        Self
54    }
55}
56
57impl ResponseWriterLayer<UnboundedSender<Response>> {
58    /// Create a new [`ResponseWriterLayer`] that prints responses to an [`AsyncWrite`]r
59    /// over an unbounded channel
60    pub fn writer_unbounded<W>(executor: &Executor, mut writer: W, mode: Option<WriterMode>) -> Self
61    where
62        W: AsyncWrite + Unpin + Send + Sync + 'static,
63    {
64        let (tx, mut rx) = unbounded_channel();
65        let (write_headers, write_body) = write_headers_body_flags(mode);
66
67        let span =
68            tracing::trace_root_span!("TrafficWriter::response::unbounded", otel.kind = "consumer");
69
70        executor.spawn_task(
71            async move {
72                while let Some(res) = rx.recv().await {
73                    if let Err(err) =
74                        write_http_response(&mut writer, res, write_headers, write_body).await
75                    {
76                        tracing::error!("failed to write http response to writer: {err:?}")
77                    }
78                }
79            }
80            .instrument(span),
81        );
82
83        Self { writer: tx }
84    }
85
86    /// Create a new [`ResponseWriterLayer`] that prints responses to stdout
87    /// over an unbounded channel.
88    #[must_use]
89    pub fn stdout_unbounded(executor: &Executor, mode: Option<WriterMode>) -> Self {
90        Self::writer_unbounded(executor, stdout(), mode)
91    }
92
93    /// Create a new [`ResponseWriterLayer`] that prints responses to stderr
94    /// over an unbounded channel.
95    #[must_use]
96    pub fn stderr_unbounded(executor: &Executor, mode: Option<WriterMode>) -> Self {
97        Self::writer_unbounded(executor, stderr(), mode)
98    }
99}
100
101impl ResponseWriterLayer<Sender<Response>> {
102    /// Create a new [`ResponseWriterLayer`] that prints responses to an [`AsyncWrite`]r
103    /// over a bounded channel with a fixed buffer size.
104    pub fn writer<W>(
105        executor: &Executor,
106        mut writer: W,
107        buffer_size: usize,
108        mode: Option<WriterMode>,
109    ) -> Self
110    where
111        W: AsyncWrite + Unpin + Send + Sync + 'static,
112    {
113        let (tx, mut rx) = channel(buffer_size);
114        let (write_headers, write_body) = write_headers_body_flags(mode);
115
116        let span =
117            tracing::trace_root_span!("TrafficWriter::response::bounded", otel.kind = "consumer");
118
119        executor.spawn_task(
120            async move {
121                while let Some(res) = rx.recv().await {
122                    if let Err(err) =
123                        write_http_response(&mut writer, res, write_headers, write_body).await
124                    {
125                        tracing::error!("failed to write http response to writer: {err:?}")
126                    }
127                }
128            }
129            .instrument(span),
130        );
131        Self { writer: tx }
132    }
133
134    /// Create a new [`ResponseWriterLayer`] that prints responses to stdout
135    /// over a bounded channel with a fixed buffer size.
136    #[must_use]
137    pub fn stdout(executor: &Executor, buffer_size: usize, mode: Option<WriterMode>) -> Self {
138        Self::writer(executor, stdout(), buffer_size, mode)
139    }
140
141    /// Create a new [`ResponseWriterLayer`] that prints responses to stderr
142    /// over a bounded channel with a fixed buffer size.
143    #[must_use]
144    pub fn stderr(executor: &Executor, buffer_size: usize, mode: Option<WriterMode>) -> Self {
145        Self::writer(executor, stderr(), buffer_size, mode)
146    }
147}
148
149impl<S, W: Clone> Layer<S> for ResponseWriterLayer<W> {
150    type Service = ResponseWriterService<S, W>;
151
152    fn layer(&self, inner: S) -> Self::Service {
153        ResponseWriterService {
154            inner,
155            writer: self.writer.clone(),
156        }
157    }
158
159    fn into_layer(self, inner: S) -> Self::Service {
160        ResponseWriterService {
161            inner,
162            writer: self.writer,
163        }
164    }
165}
166
167/// Middleware to print Http request in std format.
168///
169/// See the [module docs](super) for more details.
170#[derive(Clone)]
171pub struct ResponseWriterService<S, W> {
172    inner: S,
173    writer: W,
174}
175
176impl<S, W> ResponseWriterService<S, W> {
177    /// Create a new [`ResponseWriterService`] with a custom [`ResponseWriter`].
178    pub const fn new(writer: W, inner: S) -> Self {
179        Self { inner, writer }
180    }
181
182    define_inner_service_accessors!();
183}
184
185impl<S: Debug, W> Debug for ResponseWriterService<S, W> {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        f.debug_struct("ResponseWriterService")
188            .field("inner", &self.inner)
189            .field("writer", &format_args!("{}", std::any::type_name::<W>()))
190            .finish()
191    }
192}
193
194impl<S> ResponseWriterService<S, UnboundedSender<Response>> {
195    /// Create a new [`ResponseWriterService`] that prints responses to an [`AsyncWrite`]r
196    /// over an unbounded channel
197    pub fn writer_unbounded<W>(
198        executor: &Executor,
199        writer: W,
200        mode: Option<WriterMode>,
201        inner: S,
202    ) -> Self
203    where
204        W: AsyncWrite + Unpin + Send + Sync + 'static,
205    {
206        let layer = ResponseWriterLayer::writer_unbounded(executor, writer, mode);
207        layer.into_layer(inner)
208    }
209
210    /// Create a new [`ResponseWriterService`] that prints responses to stdout
211    /// over an unbounded channel.
212    pub fn stdout_unbounded(executor: &Executor, mode: Option<WriterMode>, inner: S) -> Self {
213        Self::writer_unbounded(executor, stdout(), mode, inner)
214    }
215
216    /// Create a new [`ResponseWriterService`] that prints responses to stderr
217    /// over an unbounded channel.
218    pub fn stderr_unbounded(executor: &Executor, mode: Option<WriterMode>, inner: S) -> Self {
219        Self::writer_unbounded(executor, stderr(), mode, inner)
220    }
221}
222
223impl<S> ResponseWriterService<S, Sender<Response>> {
224    /// Create a new [`ResponseWriterService`] that prints responses to an [`AsyncWrite`]r
225    /// over a bounded channel with a fixed buffer size.
226    pub fn writer<W>(
227        executor: &Executor,
228        writer: W,
229        buffer_size: usize,
230        mode: Option<WriterMode>,
231        inner: S,
232    ) -> Self
233    where
234        W: AsyncWrite + Unpin + Send + Sync + 'static,
235    {
236        let layer = ResponseWriterLayer::writer(executor, writer, buffer_size, mode);
237        layer.into_layer(inner)
238    }
239
240    /// Create a new [`ResponseWriterService`] that prints responses to stdout
241    /// over a bounded channel with a fixed buffer size.
242    pub fn stdout(
243        executor: &Executor,
244        buffer_size: usize,
245        mode: Option<WriterMode>,
246        inner: S,
247    ) -> Self {
248        Self::writer(executor, stdout(), buffer_size, mode, inner)
249    }
250
251    /// Create a new [`ResponseWriterService`] that prints responses to stderr
252    /// over a bounded channel with a fixed buffer size.
253    pub fn stderr(
254        executor: &Executor,
255        buffer_size: usize,
256        mode: Option<WriterMode>,
257        inner: S,
258    ) -> Self {
259        Self::writer(executor, stderr(), buffer_size, mode, inner)
260    }
261}
262
263impl<S, W> ResponseWriterService<S, W> {}
264
265impl<S, W, ReqBody, ResBody> Service<Request<ReqBody>> for ResponseWriterService<S, W>
266where
267    S: Service<Request<ReqBody>, Output = Response<ResBody>, Error: Into<BoxError>>,
268    W: ResponseWriter,
269    ReqBody: Send + 'static,
270    ResBody: StreamingBody<Data = Bytes, Error: Into<BoxError>> + Send + Sync + 'static,
271{
272    type Output = Response;
273    type Error = BoxError;
274
275    async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
276        let do_not_print_response: Option<Arc<DoNotWriteResponse>> = req.extensions().get_arc();
277        let resp = self.inner.serve(req).await.into_box_error()?;
278        let resp = if do_not_print_response.is_some() {
279            resp.map(Body::new)
280        } else {
281            let (parts, body) = resp.into_parts();
282            let body_bytes = body
283                .collect()
284                .await
285                .context("printer prepare: collect response body")?
286                .to_bytes();
287            let resp: rama_http_types::Response<Body> =
288                Response::from_parts(parts.clone(), Body::from(body_bytes.clone()));
289            self.writer.write_response(resp).await;
290            Response::from_parts(parts, Body::from(body_bytes))
291        };
292        Ok(resp)
293    }
294}
295
296impl ResponseWriter for Sender<Response> {
297    async fn write_response(&self, res: Response) {
298        if let Err(err) = self.send(res).await {
299            tracing::error!("failed to send response to channel: {err:?}")
300        }
301    }
302}
303
304impl ResponseWriter for UnboundedSender<Response> {
305    async fn write_response(&self, res: Response) {
306        if let Err(err) = self.send(res) {
307            tracing::error!("failed to send response to unbounded channel: {err:?}")
308        }
309    }
310}
311
312impl<F, Fut> ResponseWriter for F
313where
314    F: Fn(Response) -> Fut + Send + Sync + 'static,
315    Fut: Future<Output = ()> + Send + 'static,
316{
317    async fn write_response(&self, res: Response) {
318        self(res).await
319    }
320}