tonic/response.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
25use http::Extensions;
26
27use crate::metadata::MetadataMap;
28
29/// A gRPC response and metadata from an RPC call.
30#[derive(Debug)]
31pub struct Response<T> {
32 metadata: MetadataMap,
33 message: T,
34 extensions: Extensions,
35}
36
37impl<T> Response<T> {
38 /// Create a new gRPC response.
39 ///
40 /// ```rust
41 /// # use tonic::Response;
42 /// # pub struct HelloReply {
43 /// # pub message: String,
44 /// # }
45 /// # let name = "";
46 /// Response::new(HelloReply {
47 /// message: format!("Hello, {}!", name).into(),
48 /// });
49 /// ```
50 pub fn new(message: T) -> Self {
51 Response {
52 metadata: MetadataMap::new(),
53 message,
54 extensions: Extensions::new(),
55 }
56 }
57
58 /// Get a immutable reference to `T`.
59 pub fn get_ref(&self) -> &T {
60 &self.message
61 }
62
63 /// Get a mutable reference to the message
64 pub fn get_mut(&mut self) -> &mut T {
65 &mut self.message
66 }
67
68 /// Get a reference to the custom response metadata.
69 pub fn metadata(&self) -> &MetadataMap {
70 &self.metadata
71 }
72
73 /// Get a mutable reference to the response metadata.
74 pub fn metadata_mut(&mut self) -> &mut MetadataMap {
75 &mut self.metadata
76 }
77
78 /// Consumes `self`, returning the message
79 pub fn into_inner(self) -> T {
80 self.message
81 }
82
83 /// Consumes `self` returning the parts of the response.
84 pub fn into_parts(self) -> (MetadataMap, T, Extensions) {
85 (self.metadata, self.message, self.extensions)
86 }
87
88 /// Create a new gRPC response from metadata, message and extensions.
89 pub fn from_parts(metadata: MetadataMap, message: T, extensions: Extensions) -> Self {
90 Self {
91 metadata,
92 message,
93 extensions,
94 }
95 }
96
97 pub(crate) fn from_http(res: http::Response<T>) -> Self {
98 let (head, message) = res.into_parts();
99 Response {
100 metadata: MetadataMap::from_headers(head.headers),
101 message,
102 extensions: head.extensions,
103 }
104 }
105
106 pub(crate) fn into_http(self) -> http::Response<T> {
107 let mut res = http::Response::new(self.message);
108
109 *res.version_mut() = http::Version::HTTP_2;
110 *res.headers_mut() = self.metadata.into_sanitized_headers();
111 *res.extensions_mut() = self.extensions;
112
113 res
114 }
115
116 #[doc(hidden)]
117 pub fn map<F, U>(self, f: F) -> Response<U>
118 where
119 F: FnOnce(T) -> U,
120 {
121 let message = f(self.message);
122 Response {
123 metadata: self.metadata,
124 message,
125 extensions: self.extensions,
126 }
127 }
128
129 /// Returns a reference to the associated extensions.
130 pub fn extensions(&self) -> &Extensions {
131 &self.extensions
132 }
133
134 /// Returns a mutable reference to the associated extensions.
135 pub fn extensions_mut(&mut self) -> &mut Extensions {
136 &mut self.extensions
137 }
138
139 /// Disable compression of the response body.
140 ///
141 /// This disables compression of the body of this response, even if compression is enabled on
142 /// the server.
143 ///
144 /// **Note**: This only has effect on responses to unary requests and responses to client to
145 /// server streams. Response streams (server to client stream and bidirectional streams) will
146 /// still be compressed according to the configuration of the server.
147 #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
148 pub fn disable_compression(&mut self) {
149 self.extensions_mut()
150 .insert(crate::codec::compression::SingleMessageCompressionOverride::Disable);
151 }
152}
153
154impl<T> From<T> for Response<T> {
155 fn from(inner: T) -> Self {
156 Response::new(inner)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::metadata::{MetadataKey, MetadataValue};
164
165 #[test]
166 fn reserved_headers_are_excluded() {
167 let mut r = Response::new(1);
168
169 for header in &MetadataMap::GRPC_RESERVED_HEADERS {
170 r.metadata_mut().insert(
171 MetadataKey::unchecked_from_header_name(header.clone()),
172 MetadataValue::from_static("invalid"),
173 );
174 }
175
176 let http_response = r.into_http();
177 assert!(http_response.headers().is_empty());
178 }
179}