1#![expect(
2 clippy::allow_attributes,
3 reason = "macro-generated `#[allow]` attributes whose underlying lints fire only for some expansions"
4)]
5
6use super::{IntoResponseParts, ResponseParts};
7use crate::Response;
8use crate::body::{Body, Frame, SizeHint, StreamingBody};
9use crate::service::web::response::Headers;
10use crate::{
11 StatusCode,
12 header::{self, HeaderMap, HeaderName, HeaderValue},
13};
14use rama_core::bytes::{Buf, Bytes, BytesMut, buf::Chain};
15use rama_core::error::BoxError;
16use rama_core::extensions::{Extensions, ExtensionsRef};
17use rama_core::telemetry::tracing;
18use rama_http_headers::{ContentDisposition, ContentLength, ContentType, HeaderMapExt};
19use rama_http_types::InfiniteReader;
20use rama_http_types::mime;
21use rama_utils::macros::all_the_tuples_no_last_special_case;
22use rama_utils::str::arcstr::ArcStr;
23use std::{
24 borrow::Cow,
25 convert::Infallible,
26 fmt,
27 pin::Pin,
28 task::{Context, Poll},
29};
30
31macro_rules! impl_into_response_sized_body {
35 ($t:ty, $content_type:expr) => {
36 impl IntoResponse for $t {
37 fn into_response(self) -> Response {
38 let len = self.len();
39 let mut res = Body::from(self).into_response();
40 res.headers_mut().typed_insert($content_type);
41 res.headers_mut().typed_insert(ContentLength(len as u64));
42 res
43 }
44 }
45 };
46}
47
48pub trait IntoResponse {
57 fn into_response(self) -> Response;
59}
60
61#[derive(Debug, Clone)]
64pub struct StaticResponseFactory<T>(pub T);
65
66impl<T: IntoResponse> From<StaticResponseFactory<T>> for Response {
67 fn from(value: StaticResponseFactory<T>) -> Self {
68 value.0.into_response()
69 }
70}
71
72impl IntoResponse for StatusCode {
73 fn into_response(self) -> Response {
74 let mut res = ().into_response();
75 *res.status_mut() = self;
76 res
77 }
78}
79
80impl IntoResponse for () {
81 fn into_response(self) -> Response {
82 Body::empty().into_response()
83 }
84}
85
86impl IntoResponse for Infallible {
87 fn into_response(self) -> Response {
88 match self {}
89 }
90}
91
92impl IntoResponse for BoxError {
93 fn into_response(self) -> Response {
95 tracing::debug!("unexpected error in HTTP handler: {self}; return 500 status code");
96 StatusCode::INTERNAL_SERVER_ERROR.into_response()
97 }
98}
99
100impl IntoResponse for rama_core::layer::limit::policy::RateLimitReached {
101 fn into_response(self) -> Response {
102 let mut response = StatusCode::TOO_MANY_REQUESTS.into_response();
103 let secs = self.retry_after.as_secs() + u64::from(self.retry_after.subsec_nanos() > 0);
104 response
105 .headers_mut()
106 .typed_insert(rama_http_headers::RetryAfter::delay(
107 rama_http_headers::util::Seconds::new(secs),
108 ));
109 response
110 }
111}
112
113impl<B> IntoResponse for Response<B>
114where
115 B: StreamingBody<Data = Bytes, Error: Into<BoxError>> + Send + Sync + 'static,
116{
117 fn into_response(self) -> Response {
118 self.map(Body::new)
119 }
120}
121
122impl IntoResponse for crate::response::Parts {
123 fn into_response(self) -> Response {
124 Response::from_parts(self, Body::empty())
125 }
126}
127
128impl IntoResponse for Body {
129 fn into_response(self) -> Response {
130 Response::new(self)
131 }
132}
133
134impl IntoResponse for &'static str {
135 #[inline(always)]
136 fn into_response(self) -> Response {
137 Cow::Borrowed(self).into_response()
138 }
139}
140
141impl IntoResponse for String {
142 #[inline(always)]
143 fn into_response(self) -> Response {
144 Cow::<'static, str>::Owned(self).into_response()
145 }
146}
147
148impl IntoResponse for Box<str> {
149 #[inline(always)]
150 fn into_response(self) -> Response {
151 String::from(self).into_response()
152 }
153}
154
155impl_into_response_sized_body!(Cow<'static, str>, ContentType::text_utf8());
156impl_into_response_sized_body!(ArcStr, ContentType::text_utf8());
157impl_into_response_sized_body!(&ArcStr, ContentType::text_utf8());
158impl_into_response_sized_body!(Bytes, ContentType::octet_stream());
159
160impl IntoResponse for BytesMut {
161 fn into_response(self) -> Response {
162 self.freeze().into_response()
163 }
164}
165
166impl IntoResponse for InfiniteReader {
167 fn into_response(self) -> Response {
168 (
169 Headers((ContentDisposition::inline(), ContentType::octet_stream())),
170 self.into_body(),
171 )
172 .into_response()
173 }
174}
175
176impl<T, U> IntoResponse for Chain<T, U>
177where
178 T: Buf + Unpin + Send + Sync + 'static,
179 U: Buf + Unpin + Send + Sync + 'static,
180{
181 fn into_response(self) -> Response {
182 let (first, second) = self.into_inner();
183 let mut res = Response::new(Body::new(BytesChainBody {
184 first: Some(first),
185 second: Some(second),
186 }));
187 res.headers_mut().insert(
188 header::CONTENT_TYPE,
189 HeaderValue::from_static(mime::APPLICATION_OCTET_STREAM.as_ref()),
190 );
191 res
192 }
193}
194
195struct BytesChainBody<T, U> {
196 first: Option<T>,
197 second: Option<U>,
198}
199
200impl<T, U> StreamingBody for BytesChainBody<T, U>
201where
202 T: Buf + Unpin,
203 U: Buf + Unpin,
204{
205 type Data = Bytes;
206 type Error = Infallible;
207
208 fn poll_frame(
209 mut self: Pin<&mut Self>,
210 _cx: &mut Context<'_>,
211 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
212 if let Some(mut buf) = self.first.take() {
213 let bytes = buf.copy_to_bytes(buf.remaining());
214 return Poll::Ready(Some(Ok(Frame::data(bytes))));
215 }
216
217 if let Some(mut buf) = self.second.take() {
218 let bytes = buf.copy_to_bytes(buf.remaining());
219 return Poll::Ready(Some(Ok(Frame::data(bytes))));
220 }
221
222 Poll::Ready(None)
223 }
224
225 fn is_end_stream(&self) -> bool {
226 self.first.is_none() && self.second.is_none()
227 }
228
229 fn size_hint(&self) -> SizeHint {
230 match (self.first.as_ref(), self.second.as_ref()) {
231 (Some(first), Some(second)) => {
232 let total_size = first.remaining() + second.remaining();
233 SizeHint::with_exact(total_size as u64)
234 }
235 (Some(buf), None) => SizeHint::with_exact(buf.remaining() as u64),
236 (None, Some(buf)) => SizeHint::with_exact(buf.remaining() as u64),
237 (None, None) => SizeHint::with_exact(0),
238 }
239 }
240}
241
242impl IntoResponse for &'static [u8] {
243 fn into_response(self) -> Response {
244 Cow::Borrowed(self).into_response()
245 }
246}
247
248impl<const N: usize> IntoResponse for &'static [u8; N] {
249 #[inline(always)]
250 fn into_response(self) -> Response {
251 self.as_slice().into_response()
252 }
253}
254
255impl<const N: usize> IntoResponse for [u8; N] {
256 #[inline(always)]
257 fn into_response(self) -> Response {
258 self.to_vec().into_response()
259 }
260}
261
262impl IntoResponse for Vec<u8> {
263 fn into_response(self) -> Response {
264 Cow::<'static, [u8]>::Owned(self).into_response()
265 }
266}
267
268impl IntoResponse for Box<[u8]> {
269 fn into_response(self) -> Response {
270 Vec::from(self).into_response()
271 }
272}
273
274impl_into_response_sized_body!(Cow<'static, [u8]>, ContentType::octet_stream());
275
276impl<R> IntoResponse for (StatusCode, R)
277where
278 R: IntoResponse,
279{
280 fn into_response(self) -> Response {
281 let mut res = self.1.into_response();
282 *res.status_mut() = self.0;
283 res
284 }
285}
286
287impl IntoResponse for HeaderMap {
288 fn into_response(self) -> Response {
289 let mut res = ().into_response();
290 *res.headers_mut() = self;
291 res
292 }
293}
294
295impl IntoResponse for Extensions {
296 fn into_response(self) -> Response {
297 let res = ().into_response();
298 res.extensions().extend(&self);
299 res
300 }
301}
302
303impl<K, V, const N: usize> IntoResponse for [(K, V); N]
304where
305 K: TryInto<HeaderName, Error: fmt::Display>,
306 V: TryInto<HeaderValue, Error: fmt::Display>,
307{
308 fn into_response(self) -> Response {
309 (self, ()).into_response()
310 }
311}
312
313impl<R> IntoResponse for (crate::response::Parts, R)
314where
315 R: IntoResponse,
316{
317 fn into_response(self) -> Response {
318 let (parts, res) = self;
319 (parts.status, parts.headers, parts.extensions, res).into_response()
320 }
321}
322
323impl<R> IntoResponse for (crate::response::Response<()>, R)
324where
325 R: IntoResponse,
326{
327 fn into_response(self) -> Response {
328 let (template, res) = self;
329 let (parts, ()) = template.into_parts();
330 (parts, res).into_response()
331 }
332}
333
334impl<R> IntoResponse for (R,)
335where
336 R: IntoResponse,
337{
338 fn into_response(self) -> Response {
339 let (res,) = self;
340 res.into_response()
341 }
342}
343
344macro_rules! impl_into_response {
345 ( $($ty:ident),* $(,)? ) => {
346 #[allow(non_snake_case)]
347 impl<R, $($ty,)*> IntoResponse for ($($ty),*, R)
348 where
349 $( $ty: IntoResponseParts, )*
350 R: IntoResponse,
351 {
352 fn into_response(self) -> Response {
353 let ($($ty),*, res) = self;
354
355 let res = res.into_response();
356 let parts = ResponseParts { res };
357
358 $(
359 let parts = match $ty.into_response_parts(parts) {
360 Ok(parts) => parts,
361 Err(err) => {
362 return err.into_response();
363 }
364 };
365 )*
366
367 parts.res
368 }
369 }
370
371 #[allow(non_snake_case)]
372 impl<R, $($ty,)*> IntoResponse for (StatusCode, $($ty),*, R)
373 where
374 $( $ty: IntoResponseParts, )*
375 R: IntoResponse,
376 {
377 fn into_response(self) -> Response {
378 let (status, $($ty),*, res) = self;
379
380 let res = res.into_response();
381 let parts = ResponseParts { res };
382
383 $(
384 let parts = match $ty.into_response_parts(parts) {
385 Ok(parts) => parts,
386 Err(err) => {
387 return err.into_response();
388 }
389 };
390 )*
391
392 (status, parts.res).into_response()
393 }
394 }
395
396 #[allow(non_snake_case)]
397 impl<R, $($ty,)*> IntoResponse for (crate::response::Parts, $($ty),*, R)
398 where
399 $( $ty: IntoResponseParts, )*
400 R: IntoResponse,
401 {
402 fn into_response(self) -> Response {
403 let (outer_parts, $($ty),*, res) = self;
404
405 let res = res.into_response();
406 let parts = ResponseParts { res };
407 $(
408 let parts = match $ty.into_response_parts(parts) {
409 Ok(parts) => parts,
410 Err(err) => {
411 return err.into_response();
412 }
413 };
414 )*
415
416 (outer_parts, parts.res).into_response()
417 }
418 }
419
420 #[allow(non_snake_case)]
421 impl<R, $($ty,)*> IntoResponse for (crate::response::Response<()>, $($ty),*, R)
422 where
423 $( $ty: IntoResponseParts, )*
424 R: IntoResponse,
425 {
426 fn into_response(self) -> Response {
427 let (template, $($ty),*, res) = self;
428 let (parts, ()) = template.into_parts();
429 (parts, $($ty),*, res).into_response()
430 }
431 }
432 }
433}
434
435all_the_tuples_no_last_special_case!(impl_into_response);
436
437macro_rules! impl_into_response_either {
438 ($id:ident, $($param:ident),+ $(,)?) => {
439 impl<$($param),+> IntoResponse for rama_core::combinators::$id<$($param),+>
440 where
441 $($param: IntoResponse),+
442 {
443 fn into_response(self) -> Response {
444 match self {
445 $(
446 rama_core::combinators::$id::$param(val) => val.into_response(),
447 )+
448 }
449 }
450 }
451 };
452}
453
454rama_core::combinators::impl_either!(impl_into_response_either);
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459 use rama_core::combinators::Either;
460 use rama_http_types::body::util::BodyExt as _;
461 use rama_utils::str::arcstr::arcstr;
462
463 #[test]
464 fn test_either_into_response() {
465 let left: Either<&'static str, Vec<u8>> = Either::A("hello");
466 let right: Either<&'static str, Vec<u8>> = Either::B(vec![1, 2, 3]);
467
468 let left_res = left.into_response();
469 assert_eq!(
470 left_res.headers().get(header::CONTENT_TYPE).unwrap(),
471 mime::TEXT_PLAIN_UTF_8.as_ref()
472 );
473
474 let right_res = right.into_response();
475 assert_eq!(
476 right_res.headers().get(header::CONTENT_TYPE).unwrap(),
477 mime::APPLICATION_OCTET_STREAM.as_ref()
478 );
479 }
480
481 #[test]
482 fn test_either3_into_response() {
483 use rama_core::combinators::Either3;
484
485 let a: Either3<&'static str, Vec<u8>, StatusCode> = Either3::A("hello");
486 let b: Either3<&'static str, Vec<u8>, StatusCode> = Either3::B(vec![1, 2, 3]);
487 let c: Either3<&'static str, Vec<u8>, StatusCode> = Either3::C(StatusCode::NOT_FOUND);
488
489 let a_res = a.into_response();
490 assert_eq!(
491 a_res.headers().get(header::CONTENT_TYPE).unwrap(),
492 mime::TEXT_PLAIN_UTF_8.as_ref()
493 );
494
495 let b_res = b.into_response();
496 assert_eq!(
497 b_res.headers().get(header::CONTENT_TYPE).unwrap(),
498 mime::APPLICATION_OCTET_STREAM.as_ref()
499 );
500
501 let c_res = c.into_response();
502 assert_eq!(c_res.status(), StatusCode::NOT_FOUND);
503 }
504
505 macro_rules! test_content_length_content_type {
506 ($val:expr, $len:expr, $ct:expr) => {{
507 let n = $len;
508 let resp = $val.into_response();
509 let content_length: usize = resp
510 .headers()
511 .get("content-length")
512 .unwrap()
513 .to_str()
514 .unwrap()
515 .parse()
516 .unwrap();
517 assert_eq!(n, content_length);
518 let ct: ContentType = resp.headers().typed_get().unwrap();
519 assert_eq!($ct, ct);
520 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
521 assert_eq!(n, bytes.len());
522 }};
523 }
524
525 #[tokio::test]
526 async fn test_content_length_types_into_response() {
527 test_content_length_content_type!("str", 3, ContentType::text_utf8());
528 test_content_length_content_type!("string".to_owned(), 6, ContentType::text_utf8());
529 test_content_length_content_type!(
530 Cow::Borrowed("Cow::Borrowed"),
531 13,
532 ContentType::text_utf8()
533 );
534 test_content_length_content_type!(
535 Cow::Borrowed("Cow::Owned").into_owned(),
536 10,
537 ContentType::text_utf8()
538 );
539 test_content_length_content_type!(
540 Bytes::from_static(b"Bytes::from_static"),
541 18,
542 ContentType::octet_stream()
543 );
544 test_content_length_content_type!(
545 Bytes::from("Bytes::from"),
546 11,
547 ContentType::octet_stream()
548 );
549 test_content_length_content_type!(b"&[u8]", 5, ContentType::octet_stream());
550 test_content_length_content_type!(*b"[u8]", 4, ContentType::octet_stream());
551 test_content_length_content_type!(b"Vec<u8>".to_vec(), 7, ContentType::octet_stream());
552 test_content_length_content_type!(
553 Cow::Borrowed(b"Cow::Borrowed::<u8>"),
554 19,
555 ContentType::octet_stream()
556 );
557 test_content_length_content_type!(
558 Cow::Borrowed(b"Cow::Owned::<u8>").into_owned(),
559 16,
560 ContentType::octet_stream()
561 );
562 test_content_length_content_type!(arcstr!("ArcStr"), 6, ContentType::text_utf8());
563 }
564}