1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::{metadata::MetadataMap, Extensions};

/// A gRPC response and metadata from an RPC call.
#[derive(Debug)]
pub struct Response<T> {
    metadata: MetadataMap,
    message: T,
    extensions: Extensions,
}

impl<T> Response<T> {
    /// Create a new gRPC response.
    ///
    /// ```rust
    /// # use tonic::Response;
    /// # pub struct HelloReply {
    /// #   pub message: String,
    /// # }
    /// # let name = "";
    /// Response::new(HelloReply {
    ///     message: format!("Hello, {}!", name).into(),
    /// });
    /// ```
    pub fn new(message: T) -> Self {
        Response {
            metadata: MetadataMap::new(),
            message,
            extensions: Extensions::new(),
        }
    }

    /// Get a immutable reference to `T`.
    pub fn get_ref(&self) -> &T {
        &self.message
    }

    /// Get a mutable reference to the message
    pub fn get_mut(&mut self) -> &mut T {
        &mut self.message
    }

    /// Get a reference to the custom response metadata.
    pub fn metadata(&self) -> &MetadataMap {
        &self.metadata
    }

    /// Get a mutable reference to the response metadata.
    pub fn metadata_mut(&mut self) -> &mut MetadataMap {
        &mut self.metadata
    }

    /// Consumes `self`, returning the message
    pub fn into_inner(self) -> T {
        self.message
    }

    /// Consumes `self` returning the parts of the response.
    pub fn into_parts(self) -> (MetadataMap, T, Extensions) {
        (self.metadata, self.message, self.extensions)
    }

    /// Create a new gRPC response from metadata, message and extensions.
    pub fn from_parts(metadata: MetadataMap, message: T, extensions: Extensions) -> Self {
        Self {
            metadata,
            message,
            extensions,
        }
    }

    pub(crate) fn from_http(res: http::Response<T>) -> Self {
        let (head, message) = res.into_parts();
        Response {
            metadata: MetadataMap::from_headers(head.headers),
            message,
            extensions: Extensions::from_http(head.extensions),
        }
    }

    pub(crate) fn into_http(self) -> http::Response<T> {
        let mut res = http::Response::new(self.message);

        *res.version_mut() = http::Version::HTTP_2;
        *res.headers_mut() = self.metadata.into_sanitized_headers();
        *res.extensions_mut() = self.extensions.into_http();

        res
    }

    #[doc(hidden)]
    pub fn map<F, U>(self, f: F) -> Response<U>
    where
        F: FnOnce(T) -> U,
    {
        let message = f(self.message);
        Response {
            metadata: self.metadata,
            message,
            extensions: self.extensions,
        }
    }

    /// Returns a reference to the associated extensions.
    pub fn extensions(&self) -> &Extensions {
        &self.extensions
    }

    /// Returns a mutable reference to the associated extensions.
    pub fn extensions_mut(&mut self) -> &mut Extensions {
        &mut self.extensions
    }

    /// Disable compression of the response body.
    ///
    /// This disables compression of the body of this response, even if compression is enabled on
    /// the server.
    ///
    /// **Note**: This only has effect on responses to unary requests and responses to client to
    /// server streams. Response streams (server to client stream and bidirectional streams) will
    /// still be compressed according to the configuration of the server.
    #[cfg(feature = "gzip")]
    #[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
    pub fn disable_compression(&mut self) {
        self.extensions_mut()
            .insert(crate::codec::compression::SingleMessageCompressionOverride::Disable);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::metadata::MetadataValue;

    #[test]
    fn reserved_headers_are_excluded() {
        let mut r = Response::new(1);

        for header in &MetadataMap::GRPC_RESERVED_HEADERS {
            r.metadata_mut()
                .insert(*header, MetadataValue::from_static("invalid"));
        }

        let http_response = r.into_http();
        assert!(http_response.headers().is_empty());
    }
}