tonic/
response.rs

1use crate::{metadata::MetadataMap, Extensions};
2
3/// A gRPC response and metadata from an RPC call.
4#[derive(Debug)]
5pub struct Response<T> {
6    metadata: MetadataMap,
7    message: T,
8    extensions: Extensions,
9}
10
11impl<T> Response<T> {
12    /// Create a new gRPC response.
13    ///
14    /// ```rust
15    /// # use tonic::Response;
16    /// # pub struct HelloReply {
17    /// #   pub message: String,
18    /// # }
19    /// # let name = "";
20    /// Response::new(HelloReply {
21    ///     message: format!("Hello, {}!", name).into(),
22    /// });
23    /// ```
24    pub fn new(message: T) -> Self {
25        Response {
26            metadata: MetadataMap::new(),
27            message,
28            extensions: Extensions::new(),
29        }
30    }
31
32    /// Get a immutable reference to `T`.
33    pub fn get_ref(&self) -> &T {
34        &self.message
35    }
36
37    /// Get a mutable reference to the message
38    pub fn get_mut(&mut self) -> &mut T {
39        &mut self.message
40    }
41
42    /// Get a reference to the custom response metadata.
43    pub fn metadata(&self) -> &MetadataMap {
44        &self.metadata
45    }
46
47    /// Get a mutable reference to the response metadata.
48    pub fn metadata_mut(&mut self) -> &mut MetadataMap {
49        &mut self.metadata
50    }
51
52    /// Consumes `self`, returning the message
53    pub fn into_inner(self) -> T {
54        self.message
55    }
56
57    pub(crate) fn into_parts(self) -> (MetadataMap, T, Extensions) {
58        (self.metadata, self.message, self.extensions)
59    }
60
61    pub(crate) fn from_parts(metadata: MetadataMap, message: T, extensions: Extensions) -> Self {
62        Self {
63            metadata,
64            message,
65            extensions,
66        }
67    }
68
69    pub(crate) fn from_http(res: http::Response<T>) -> Self {
70        let (head, message) = res.into_parts();
71        Response {
72            metadata: MetadataMap::from_headers(head.headers),
73            message,
74            extensions: Extensions::from_http(head.extensions),
75        }
76    }
77
78    pub(crate) fn into_http(self) -> http::Response<T> {
79        let mut res = http::Response::new(self.message);
80
81        *res.version_mut() = http::Version::HTTP_2;
82        *res.headers_mut() = self.metadata.into_sanitized_headers();
83        *res.extensions_mut() = self.extensions.into_http();
84
85        res
86    }
87
88    #[doc(hidden)]
89    pub fn map<F, U>(self, f: F) -> Response<U>
90    where
91        F: FnOnce(T) -> U,
92    {
93        let message = f(self.message);
94        Response {
95            metadata: self.metadata,
96            message,
97            extensions: self.extensions,
98        }
99    }
100
101    /// Returns a reference to the associated extensions.
102    pub fn extensions(&self) -> &Extensions {
103        &self.extensions
104    }
105
106    /// Returns a mutable reference to the associated extensions.
107    pub fn extensions_mut(&mut self) -> &mut Extensions {
108        &mut self.extensions
109    }
110
111    /// Disable compression of the response body.
112    ///
113    /// This disables compression of the body of this response, even if compression is enabled on
114    /// the server.
115    ///
116    /// **Note**: This only has effect on responses to unary requests and responses to client to
117    /// server streams. Response streams (server to client stream and bidirectional streams) will
118    /// still be compressed according to the configuration of the server.
119    #[cfg(feature = "compression")]
120    #[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
121    pub fn disable_compression(&mut self) {
122        self.extensions_mut()
123            .insert(crate::codec::compression::SingleMessageCompressionOverride::Disable);
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::metadata::MetadataValue;
131
132    #[test]
133    fn reserved_headers_are_excluded() {
134        let mut r = Response::new(1);
135
136        for header in &MetadataMap::GRPC_RESERVED_HEADERS {
137            r.metadata_mut()
138                .insert(*header, MetadataValue::from_static("invalid"));
139        }
140
141        let http_response = r.into_http();
142        assert!(http_response.headers().is_empty());
143    }
144}