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