Skip to main content

thrift/
configuration.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/// Configuration for Thrift protocols.
19#[derive(Debug, Clone)]
20pub struct TConfiguration {
21    max_message_size: Option<usize>,
22    max_frame_size: Option<usize>,
23    max_recursion_depth: Option<usize>,
24    max_container_size: Option<usize>,
25    max_string_size: Option<usize>,
26}
27
28impl TConfiguration {
29    // this value is used consistently across all Thrift libraries
30    pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 100 * 1024 * 1024;
31
32    // this value is used consistently across all Thrift libraries
33    pub const DEFAULT_MAX_FRAME_SIZE: usize = 16_384_000;
34
35    pub const DEFAULT_RECURSION_LIMIT: usize = 64;
36
37    pub const DEFAULT_CONTAINER_LIMIT: Option<usize> = None;
38
39    pub const DEFAULT_STRING_LIMIT: usize = 100 * 1024 * 1024;
40
41    pub fn no_limits() -> Self {
42        Self {
43            max_message_size: None,
44            max_frame_size: None,
45            max_recursion_depth: None,
46            max_container_size: None,
47            max_string_size: None,
48        }
49    }
50
51    pub fn max_message_size(&self) -> Option<usize> {
52        self.max_message_size
53    }
54
55    pub fn max_frame_size(&self) -> Option<usize> {
56        self.max_frame_size
57    }
58
59    pub fn max_recursion_depth(&self) -> Option<usize> {
60        self.max_recursion_depth
61    }
62
63    pub fn max_container_size(&self) -> Option<usize> {
64        self.max_container_size
65    }
66
67    pub fn max_string_size(&self) -> Option<usize> {
68        self.max_string_size
69    }
70
71    pub fn builder() -> TConfigurationBuilder {
72        TConfigurationBuilder::default()
73    }
74}
75
76impl Default for TConfiguration {
77    fn default() -> Self {
78        Self {
79            max_message_size: Some(Self::DEFAULT_MAX_MESSAGE_SIZE),
80            max_frame_size: Some(Self::DEFAULT_MAX_FRAME_SIZE),
81            max_recursion_depth: Some(Self::DEFAULT_RECURSION_LIMIT),
82            max_container_size: Self::DEFAULT_CONTAINER_LIMIT,
83            max_string_size: Some(Self::DEFAULT_STRING_LIMIT),
84        }
85    }
86}
87
88#[derive(Debug, Default)]
89pub struct TConfigurationBuilder {
90    config: TConfiguration,
91}
92
93impl TConfigurationBuilder {
94    pub fn max_message_size(mut self, limit: Option<usize>) -> Self {
95        self.config.max_message_size = limit;
96        self
97    }
98
99    pub fn max_frame_size(mut self, limit: Option<usize>) -> Self {
100        self.config.max_frame_size = limit;
101        self
102    }
103
104    pub fn max_recursion_depth(mut self, limit: Option<usize>) -> Self {
105        self.config.max_recursion_depth = limit;
106        self
107    }
108
109    pub fn max_container_size(mut self, limit: Option<usize>) -> Self {
110        self.config.max_container_size = limit;
111        self
112    }
113
114    pub fn max_string_size(mut self, limit: Option<usize>) -> Self {
115        self.config.max_string_size = limit;
116        self
117    }
118
119    pub fn build(self) -> crate::Result<TConfiguration> {
120        if let (Some(frame_size), Some(message_size)) =
121            (self.config.max_frame_size, self.config.max_message_size)
122        {
123            if frame_size > message_size {
124                // FIXME: This should probably be a different error type.
125                return Err(crate::Error::Application(crate::ApplicationError::new(
126                    crate::ApplicationErrorKind::Unknown,
127                    format!(
128                        "Invalid configuration: max_frame_size ({}) cannot exceed max_message_size ({})",
129                        frame_size, message_size
130                    ),
131                )));
132            }
133        }
134
135        Ok(self.config)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_custom_configuration_builder() {
145        let config = TConfiguration::builder()
146            .max_message_size(Some(1024))
147            .max_frame_size(Some(512))
148            .max_recursion_depth(Some(10))
149            .max_container_size(Some(100))
150            .max_string_size(Some(256))
151            .build()
152            .unwrap();
153
154        assert_eq!(config.max_message_size(), Some(1024));
155        assert_eq!(config.max_frame_size(), Some(512));
156        assert_eq!(config.max_recursion_depth(), Some(10));
157        assert_eq!(config.max_container_size(), Some(100));
158        assert_eq!(config.max_string_size(), Some(256));
159    }
160
161    #[test]
162    fn test_invalid_configuration() {
163        // Test that builder catches invalid configurations
164        let result = TConfiguration::builder()
165            .max_frame_size(Some(1000))
166            .max_message_size(Some(500)) // frame size > message size is invalid
167            .build();
168
169        assert!(result.is_err());
170        match result {
171            Err(crate::Error::Application(e)) => {
172                assert!(e.message.contains("max_frame_size"));
173                assert!(e.message.contains("cannot exceed max_message_size"));
174            }
175            _ => panic!("Expected Application error"),
176        }
177    }
178}