1use super::{
2 PerMessageFileWriter, TrafficWriterId, WriterMode, capture_body_channel,
3 ensure_traffic_writer_id, write_headers_body_flags,
4};
5use crate::io::write_http_response_streaming;
6use crate::{Body, Request, Response, StreamingBody, body::util::BodyExt as _};
7use rama_core::bytes::Bytes;
8use rama_core::error::{BoxError, ErrorContext as _};
9use rama_core::extensions::{Extension, ExtensionsRef};
10use rama_core::rt::Executor;
11use rama_core::telemetry::tracing::{self, Instrument};
12use rama_core::{Layer, Service};
13use rama_http_types::CaptureBody;
14use rama_utils::macros::define_inner_service_accessors;
15use std::{fmt::Debug, io, path::PathBuf, sync::Arc};
16use tokio::io::{AsyncWrite, AsyncWriteExt as _, stderr, stdout};
17use tokio::sync::mpsc::{Sender, UnboundedSender, channel, unbounded_channel};
18
19async fn write_response_entry<W>(
20 writer: &mut W,
21 response: Response,
22 write_headers: bool,
23 write_body: bool,
24) where
25 W: AsyncWrite + Unpin + Send + Sync + 'static,
26{
27 if let Err(err) =
28 write_http_response_streaming(writer, response, write_headers, write_body).await
29 {
30 tracing::error!("failed to write http response to writer: {err:?}")
31 }
32}
33
34macro_rules! drive_response_writer {
35 ($writer:ident, $rx:ident, $write_headers:ident, $write_body:ident) => {{
36 while let Some(response) = $rx.recv().await {
37 write_response_entry(&mut $writer, response, $write_headers, $write_body).await;
38 }
39 if let Err(err) = $writer.flush().await {
40 tracing::error!("failed to flush response writer: {err:?}")
41 }
42 }};
43}
44
45#[derive(Clone)]
50pub struct ResponseWriterLayer<W> {
51 writer: W,
52 executor: Executor,
53}
54
55impl<W> Debug for ResponseWriterLayer<W> {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 f.debug_struct("ResponseWriterLayer")
58 .field("writer", &format_args!("{}", std::any::type_name::<W>()))
59 .finish()
60 }
61}
62
63impl<W> ResponseWriterLayer<W> {
64 pub const fn new(writer: W) -> Self {
69 Self::new_with_executor(writer, Executor::new())
70 }
71
72 pub const fn new_with_executor(writer: W, executor: Executor) -> Self {
75 Self { writer, executor }
76 }
77}
78
79pub trait ResponseWriter: Send + Sync + 'static {
81 fn write_response(&self, res: Response) -> impl Future<Output = ()> + Send + '_;
87}
88
89#[derive(Debug, Clone, Default, Extension)]
91#[extension(tags(http))]
92#[non_exhaustive]
93pub struct DoNotWriteResponse;
94
95impl DoNotWriteResponse {
96 #[must_use]
98 pub const fn new() -> Self {
99 Self
100 }
101}
102
103impl ResponseWriterLayer<UnboundedSender<Response>> {
104 pub fn writer_unbounded<W>(executor: &Executor, mut writer: W, mode: Option<WriterMode>) -> Self
107 where
108 W: AsyncWrite + Unpin + Send + Sync + 'static,
109 {
110 let (tx, mut rx) = unbounded_channel();
111 let (write_headers, write_body) = write_headers_body_flags(mode);
112
113 let span =
114 tracing::trace_root_span!("TrafficWriter::response::unbounded", otel.kind = "consumer");
115
116 executor.spawn_task(
117 async move {
118 drive_response_writer!(writer, rx, write_headers, write_body);
119 }
120 .instrument(span),
121 );
122
123 Self {
124 writer: tx,
125 executor: executor.clone(),
126 }
127 }
128
129 #[must_use]
132 pub fn stdout_unbounded(executor: &Executor, mode: Option<WriterMode>) -> Self {
133 Self::writer_unbounded(executor, stdout(), mode)
134 }
135
136 #[must_use]
139 pub fn stderr_unbounded(executor: &Executor, mode: Option<WriterMode>) -> Self {
140 Self::writer_unbounded(executor, stderr(), mode)
141 }
142}
143
144impl ResponseWriterLayer<PerMessageFileWriter> {
145 pub async fn file_per_response(
148 executor: &Executor,
149 directory: impl Into<PathBuf>,
150 prefix: impl AsRef<str>,
151 mode: Option<WriterMode>,
152 ) -> io::Result<Self> {
153 let writer = PerMessageFileWriter::try_new(directory, prefix)
154 .await?
155 .with_request_mode(None)
156 .with_response_mode(mode);
157 Ok(Self::new_with_executor(writer, executor.clone()))
158 }
159}
160
161impl ResponseWriterLayer<Sender<Response>> {
162 pub fn writer<W>(
165 executor: &Executor,
166 mut writer: W,
167 buffer_size: usize,
168 mode: Option<WriterMode>,
169 ) -> Self
170 where
171 W: AsyncWrite + Unpin + Send + Sync + 'static,
172 {
173 let (tx, mut rx) = channel(buffer_size);
174 let (write_headers, write_body) = write_headers_body_flags(mode);
175
176 let span =
177 tracing::trace_root_span!("TrafficWriter::response::bounded", otel.kind = "consumer");
178
179 executor.spawn_task(
180 async move {
181 drive_response_writer!(writer, rx, write_headers, write_body);
182 }
183 .instrument(span),
184 );
185 Self {
186 writer: tx,
187 executor: executor.clone(),
188 }
189 }
190
191 #[must_use]
194 pub fn stdout(executor: &Executor, buffer_size: usize, mode: Option<WriterMode>) -> Self {
195 Self::writer(executor, stdout(), buffer_size, mode)
196 }
197
198 #[must_use]
201 pub fn stderr(executor: &Executor, buffer_size: usize, mode: Option<WriterMode>) -> Self {
202 Self::writer(executor, stderr(), buffer_size, mode)
203 }
204}
205
206impl<S, W: Clone> Layer<S> for ResponseWriterLayer<W> {
207 type Service = ResponseWriterService<S, W>;
208
209 fn layer(&self, inner: S) -> Self::Service {
210 ResponseWriterService {
211 inner,
212 writer: Arc::new(self.writer.clone()),
213 executor: self.executor.clone(),
214 }
215 }
216
217 fn into_layer(self, inner: S) -> Self::Service {
218 ResponseWriterService {
219 inner,
220 writer: Arc::new(self.writer),
221 executor: self.executor,
222 }
223 }
224}
225
226#[derive(Clone)]
233pub struct ResponseWriterService<S, W> {
234 inner: S,
235 writer: Arc<W>,
236 executor: Executor,
237}
238
239impl<S, W> ResponseWriterService<S, W> {
240 pub fn new(writer: W, inner: S) -> Self {
245 Self::new_with_executor(writer, inner, Executor::new())
246 }
247
248 pub fn new_with_executor(writer: W, inner: S, executor: Executor) -> Self {
251 Self {
252 inner,
253 writer: Arc::new(writer),
254 executor,
255 }
256 }
257
258 define_inner_service_accessors!();
259}
260
261impl<S: Debug, W> Debug for ResponseWriterService<S, W> {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 f.debug_struct("ResponseWriterService")
264 .field("inner", &self.inner)
265 .field("writer", &format_args!("{}", std::any::type_name::<W>()))
266 .finish()
267 }
268}
269
270impl<S> ResponseWriterService<S, UnboundedSender<Response>> {
271 pub fn writer_unbounded<W>(
274 executor: &Executor,
275 writer: W,
276 mode: Option<WriterMode>,
277 inner: S,
278 ) -> Self
279 where
280 W: AsyncWrite + Unpin + Send + Sync + 'static,
281 {
282 let layer = ResponseWriterLayer::writer_unbounded(executor, writer, mode);
283 layer.into_layer(inner)
284 }
285
286 pub fn stdout_unbounded(executor: &Executor, mode: Option<WriterMode>, inner: S) -> Self {
289 Self::writer_unbounded(executor, stdout(), mode, inner)
290 }
291
292 pub fn stderr_unbounded(executor: &Executor, mode: Option<WriterMode>, inner: S) -> Self {
295 Self::writer_unbounded(executor, stderr(), mode, inner)
296 }
297}
298
299impl<S> ResponseWriterService<S, PerMessageFileWriter> {
300 pub async fn file_per_response(
303 executor: &Executor,
304 directory: impl Into<PathBuf>,
305 prefix: impl AsRef<str>,
306 mode: Option<WriterMode>,
307 inner: S,
308 ) -> io::Result<Self> {
309 let layer =
310 ResponseWriterLayer::file_per_response(executor, directory, prefix, mode).await?;
311 Ok(layer.into_layer(inner))
312 }
313}
314
315impl<S> ResponseWriterService<S, Sender<Response>> {
316 pub fn writer<W>(
319 executor: &Executor,
320 writer: W,
321 buffer_size: usize,
322 mode: Option<WriterMode>,
323 inner: S,
324 ) -> Self
325 where
326 W: AsyncWrite + Unpin + Send + Sync + 'static,
327 {
328 let layer = ResponseWriterLayer::writer(executor, writer, buffer_size, mode);
329 layer.into_layer(inner)
330 }
331
332 pub fn stdout(
335 executor: &Executor,
336 buffer_size: usize,
337 mode: Option<WriterMode>,
338 inner: S,
339 ) -> Self {
340 Self::writer(executor, stdout(), buffer_size, mode, inner)
341 }
342
343 pub fn stderr(
346 executor: &Executor,
347 buffer_size: usize,
348 mode: Option<WriterMode>,
349 inner: S,
350 ) -> Self {
351 Self::writer(executor, stderr(), buffer_size, mode, inner)
352 }
353}
354
355impl<S, W> ResponseWriterService<S, W> {}
356
357impl<S, W, ReqBody, ResBody> Service<Request<ReqBody>> for ResponseWriterService<S, W>
358where
359 S: Service<Request<ReqBody>, Output = Response<ResBody>, Error: Into<BoxError>>,
360 W: ResponseWriter,
361 ReqBody: Send + 'static,
362 ResBody: StreamingBody<Data = Bytes, Error: Into<BoxError>> + Send + Sync + 'static,
363{
364 type Output = Response;
365 type Error = BoxError;
366
367 async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
368 let do_not_print_response: Option<Arc<DoNotWriteResponse>> = req.extensions().get_arc();
369 let traffic_writer_id = do_not_print_response
370 .is_none()
371 .then(|| ensure_traffic_writer_id(req.extensions()));
372 let resp = self.inner.serve(req).await.into_box_error()?;
373 let resp = if let Some(traffic_writer_id) = traffic_writer_id {
374 if resp.extensions().get_ref::<TrafficWriterId>().copied() != Some(traffic_writer_id) {
375 resp.extensions().insert(traffic_writer_id);
376 }
377 let (parts, body) = resp.into_parts();
378 let (capture, captured_body) = capture_body_channel();
379 let captured_parts = parts.clone();
380 let writer = Arc::clone(&self.writer);
381 self.executor.spawn_task(async move {
382 writer
383 .write_response(Response::from_parts(captured_parts, captured_body))
384 .await;
385 });
386 Response::from_parts(
387 parts,
388 Body::new(CaptureBody::new(body.map_err(Into::into), capture)),
389 )
390 } else {
391 resp.map(Body::new)
392 };
393 Ok(resp)
394 }
395}
396
397impl ResponseWriter for Sender<Response> {
398 async fn write_response(&self, res: Response) {
399 if let Err(err) = self.send(res).await {
400 tracing::error!("failed to send response to channel: {err:?}")
401 }
402 }
403}
404
405impl ResponseWriter for UnboundedSender<Response> {
406 async fn write_response(&self, res: Response) {
407 if let Err(err) = self.send(res) {
408 tracing::error!("failed to send response to unbounded channel: {err:?}")
409 }
410 }
411}
412
413impl<F, Fut> ResponseWriter for F
414where
415 F: Fn(Response) -> Fut + Send + Sync + 'static,
416 Fut: Future<Output = ()> + Send + 'static,
417{
418 async fn write_response(&self, res: Response) {
419 self(res).await
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use std::{
426 convert::Infallible,
427 sync::{
428 Arc,
429 atomic::{AtomicUsize, Ordering},
430 },
431 time::Duration,
432 };
433
434 use rama_core::{
435 Layer as _, Service as _, bytes::Bytes, extensions::ExtensionsRef as _, rt::Executor,
436 service::service_fn,
437 };
438 use tokio::io::AsyncReadExt as _;
439
440 use super::{ResponseWriterLayer, ResponseWriterService};
441 use crate::layer::traffic_writer::{TrafficWriterId, WriterMode};
442 use crate::{Body, Request, Response, body::util::BodyExt as _};
443
444 #[tokio::test]
445 async fn shared_response_writer_flushes_when_channel_closes() {
446 let executor = Executor::new();
447 let (output, mut captured) = tokio::io::duplex(128);
448 let layer = ResponseWriterLayer::writer_unbounded(
449 &executor,
450 tokio::io::BufWriter::new(output),
451 Some(WriterMode::All),
452 );
453 let sender = layer.writer.clone();
454 drop(layer);
455
456 sender.send(Response::new(Body::empty())).unwrap();
457 drop(sender);
458
459 let mut bytes = Vec::new();
460 tokio::time::timeout(Duration::from_secs(1), captured.read_to_end(&mut bytes))
461 .await
462 .expect("response writer should flush and close")
463 .unwrap();
464 assert_eq!(bytes, b"HTTP/1.1 200 OK\r\n\r\n");
465 }
466
467 #[tokio::test]
468 async fn file_constructors_only_enable_response_capture() {
469 let temp = tempfile::tempdir().unwrap();
470 let executor = Executor::new();
471 let layer = ResponseWriterLayer::file_per_response(
472 &executor,
473 temp.path(),
474 "responses",
475 Some(WriterMode::Headers),
476 )
477 .await
478 .unwrap();
479 assert_eq!(layer.writer.request_mode(), None);
480 assert_eq!(layer.writer.response_mode(), Some(WriterMode::Headers));
481
482 let service = ResponseWriterService::file_per_response(
483 &executor,
484 temp.path(),
485 "responses",
486 Some(WriterMode::Body),
487 (),
488 )
489 .await
490 .unwrap();
491 assert_eq!(service.writer.request_mode(), None);
492 assert_eq!(service.writer.response_mode(), Some(WriterMode::Body));
493 }
494
495 #[tokio::test]
496 async fn response_writer_propagates_private_request_id_to_both_responses() {
497 let (writer, mut captured_responses) = tokio::sync::mpsc::unbounded_channel();
498 let (seen_id, mut seen_ids) = tokio::sync::mpsc::unbounded_channel();
499 let inner = service_fn(move |request: Request| {
500 let seen_id = seen_id.clone();
501 async move {
502 seen_id
503 .send(*request.extensions().get_ref::<TrafficWriterId>().unwrap())
504 .unwrap();
505 Ok::<_, Infallible>(Response::new(Body::empty()))
506 }
507 });
508 let service = ResponseWriterLayer::new(writer).into_layer(inner);
509
510 let response = service.serve(Request::new(Body::empty())).await.unwrap();
511 let captured = tokio::time::timeout(Duration::from_secs(1), captured_responses.recv())
512 .await
513 .unwrap()
514 .unwrap();
515 let request_id = tokio::time::timeout(Duration::from_secs(1), seen_ids.recv())
516 .await
517 .unwrap()
518 .unwrap();
519 assert_eq!(
520 response.extensions().get_ref::<TrafficWriterId>(),
521 Some(&request_id)
522 );
523 assert_eq!(
524 captured.extensions().get_ref::<TrafficWriterId>(),
525 Some(&request_id)
526 );
527 }
528
529 #[tokio::test]
530 async fn writer_observes_response_as_the_caller_streams_it() {
531 let polls = Arc::new(AtomicUsize::new(0));
532 let response_polls = Arc::clone(&polls);
533 let (writer, mut written) = tokio::sync::mpsc::unbounded_channel();
534 let service =
535 ResponseWriterLayer::new(writer).into_layer(service_fn(move |_request: Request| {
536 let polls = Arc::clone(&response_polls);
537 async move {
538 let body = Body::from_stream(rama_core::futures::stream::once(async move {
539 polls.fetch_add(1, Ordering::Relaxed);
540 Ok::<_, Infallible>(Bytes::from_static(b"streamed response"))
541 }));
542 Ok::<_, Infallible>(Response::new(body))
543 }
544 }));
545
546 let response = service.serve(Request::new(Body::empty())).await.unwrap();
547 assert_eq!(polls.load(Ordering::Relaxed), 0);
548 written.try_recv().unwrap_err();
549
550 let written_response =
551 tokio::time::timeout(std::time::Duration::from_secs(1), written.recv())
552 .await
553 .expect("response capture should arrive promptly")
554 .unwrap();
555 let (response, captured) = tokio::time::timeout(std::time::Duration::from_secs(1), async {
556 tokio::join!(
557 response.into_body().collect(),
558 written_response.into_body().collect(),
559 )
560 })
561 .await
562 .expect("response streams should finish promptly");
563 assert_eq!(
564 response.unwrap().to_bytes(),
565 Bytes::from_static(b"streamed response")
566 );
567 assert_eq!(polls.load(Ordering::Relaxed), 1);
568 assert_eq!(
569 captured.unwrap().to_bytes(),
570 Bytes::from_static(b"streamed response")
571 );
572 }
573
574 #[tokio::test]
575 async fn shared_writer_backpressures_later_response_without_interleaving() {
576 let (first_sender, first_receiver) = tokio::sync::mpsc::unbounded_channel();
577 let first_receiver = Arc::new(tokio::sync::Mutex::new(Some(first_receiver)));
578 let inner_receiver = Arc::clone(&first_receiver);
579 let inner = service_fn(move |request: Request| {
580 let first_receiver = Arc::clone(&inner_receiver);
581 async move {
582 let body = if request.headers().contains_key("x-long-lived") {
583 let receiver = first_receiver
584 .lock()
585 .await
586 .take()
587 .expect("the long-lived response is requested once");
588 Body::from_stream(rama_core::futures::stream::unfold(
589 receiver,
590 |mut receiver| async move { receiver.recv().await.map(|item| (item, receiver)) },
591 ))
592 } else {
593 Body::from("second")
594 };
595 Ok::<_, Infallible>(Response::new(body))
596 }
597 });
598 let executor = Executor::new();
599 let (writer, mut output) = tokio::io::duplex(64);
600 let service =
601 ResponseWriterLayer::writer_unbounded(&executor, writer, Some(WriterMode::Body))
602 .into_layer(inner);
603
604 let first_request = Request::builder()
605 .header("x-long-lived", "true")
606 .body(Body::empty())
607 .unwrap();
608 let first_response = service.serve(first_request).await.unwrap();
609 let mut first_body = first_response.into_body();
610 first_sender
611 .send(Ok::<_, Infallible>(Bytes::from_static(b"first")))
612 .unwrap();
613 assert_eq!(
614 first_body
615 .frame()
616 .await
617 .unwrap()
618 .unwrap()
619 .into_data()
620 .unwrap(),
621 "first"
622 );
623
624 let mut first_output = [0; 7];
625 tokio::time::timeout(Duration::from_secs(1), output.read_exact(&mut first_output))
626 .await
627 .expect("the writer should start draining the first response")
628 .unwrap();
629 assert_eq!(&first_output, b"\r\nfirst");
630
631 let second_response = service.serve(Request::new(Body::empty())).await.unwrap();
632 let mut second = Box::pin(second_response.into_body().collect());
633 tokio::time::timeout(Duration::from_millis(50), &mut second)
634 .await
635 .expect_err("the shared writer must backpressure a later body");
636
637 drop(first_body);
638 drop(first_sender);
639 let second = tokio::time::timeout(Duration::from_secs(1), second)
640 .await
641 .expect("the later body should resume when the first capture ends")
642 .unwrap();
643 assert_eq!(second.to_bytes(), Bytes::from_static(b"second"));
644
645 let mut second_output = [0; 8];
646 tokio::time::timeout(
647 Duration::from_secs(1),
648 output.read_exact(&mut second_output),
649 )
650 .await
651 .expect("the second response should be written after the first")
652 .unwrap();
653 assert_eq!(&second_output, b"\r\nsecond");
654 }
655
656 #[tokio::test]
657 async fn headers_only_writer_does_not_consume_or_stall_live_body() {
658 let inner = service_fn(|_request: Request| async {
659 Ok::<_, Infallible>(
660 Response::builder()
661 .header("x-written", "yes")
662 .body(Body::from("live payload"))
663 .unwrap(),
664 )
665 });
666 let executor = Executor::new();
667 let (writer, mut output) = tokio::io::duplex(128);
668 let service =
669 ResponseWriterLayer::writer_unbounded(&executor, writer, Some(WriterMode::Headers))
670 .into_layer(inner);
671
672 let response = service.serve(Request::new(Body::empty())).await.unwrap();
673 assert_eq!(
674 response.into_body().collect().await.unwrap().to_bytes(),
675 Bytes::from_static(b"live payload")
676 );
677 drop(service);
678
679 let mut written = Vec::new();
680 tokio::time::timeout(Duration::from_secs(1), output.read_to_end(&mut written))
681 .await
682 .expect("the headers-only writer should shut down promptly")
683 .unwrap();
684 let written = String::from_utf8(written).unwrap();
685 assert!(written.contains("HTTP/1.1 200 OK\r\n"));
686 assert!(written.contains("x-written: yes\r\n"));
687 assert!(!written.contains("live payload"));
688 }
689}