Skip to main content

tonic/
body.rs

1/*
2 *
3 * Copyright 2025 gRPC authors.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25//! HTTP specific body utilities.
26
27use std::{pin::Pin, task::Poll};
28
29use http_body_util::BodyExt as _;
30
31// A type erased HTTP body.
32type BoxBody = http_body_util::combinators::UnsyncBoxBody<bytes::Bytes, crate::Status>;
33
34/// A body type used in `tonic`.
35#[derive(Debug)]
36pub struct Body {
37    kind: Kind,
38}
39
40#[derive(Debug)]
41enum Kind {
42    Empty,
43    Wrap(BoxBody),
44}
45
46impl Body {
47    fn from_kind(kind: Kind) -> Self {
48        Self { kind }
49    }
50
51    /// Create a new empty `Body`.
52    pub const fn empty() -> Self {
53        Self { kind: Kind::Empty }
54    }
55
56    /// Create a new `Body` from an existing `Body`.
57    pub fn new<B>(body: B) -> Self
58    where
59        B: http_body::Body<Data = bytes::Bytes> + Send + 'static,
60        B::Error: Into<crate::BoxError>,
61    {
62        if body.is_end_stream() {
63            return Self::empty();
64        }
65
66        let mut body = Some(body);
67
68        if let Some(body) = <dyn std::any::Any>::downcast_mut::<Option<Body>>(&mut body) {
69            return body.take().unwrap();
70        }
71
72        if let Some(body) = <dyn std::any::Any>::downcast_mut::<Option<BoxBody>>(&mut body) {
73            return Self::from_kind(Kind::Wrap(body.take().unwrap()));
74        }
75
76        let body = body
77            .unwrap()
78            .map_err(crate::Status::map_error)
79            .boxed_unsync();
80
81        Self::from_kind(Kind::Wrap(body))
82    }
83}
84
85impl Default for Body {
86    fn default() -> Self {
87        Self::empty()
88    }
89}
90
91impl http_body::Body for Body {
92    type Data = bytes::Bytes;
93    type Error = crate::Status;
94
95    fn poll_frame(
96        mut self: std::pin::Pin<&mut Self>,
97        cx: &mut std::task::Context<'_>,
98    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
99        match &mut self.kind {
100            Kind::Empty => Poll::Ready(None),
101            Kind::Wrap(body) => Pin::new(body).poll_frame(cx),
102        }
103    }
104
105    fn size_hint(&self) -> http_body::SizeHint {
106        match &self.kind {
107            Kind::Empty => http_body::SizeHint::with_exact(0),
108            Kind::Wrap(body) => body.size_hint(),
109        }
110    }
111
112    fn is_end_stream(&self) -> bool {
113        match &self.kind {
114            Kind::Empty => true,
115            Kind::Wrap(body) => body.is_end_stream(),
116        }
117    }
118}