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