blob: 454d9f3774829ce6df9bad8d0c803387628ae2c6 [file] [log] [blame]
David K. Bainbridgebd6b2882021-08-26 13:31:02 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package thrift
21
22import (
23 "crypto/tls"
24 "fmt"
25 "time"
26)
27
28// Default TConfiguration values.
29const (
30 DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024
31 DEFAULT_MAX_FRAME_SIZE = 16384000
32
33 DEFAULT_TBINARY_STRICT_READ = false
34 DEFAULT_TBINARY_STRICT_WRITE = true
35
36 DEFAULT_CONNECT_TIMEOUT = 0
37 DEFAULT_SOCKET_TIMEOUT = 0
38)
39
40// TConfiguration defines some configurations shared between TTransport,
41// TProtocol, TTransportFactory, TProtocolFactory, and other implementations.
42//
43// When constructing TConfiguration, you only need to specify the non-default
44// fields. All zero values have sane default values.
45//
46// Not all configurations defined are applicable to all implementations.
47// Implementations are free to ignore the configurations not applicable to them.
48//
49// All functions attached to this type are nil-safe.
50//
51// See [1] for spec.
52//
53// NOTE: When using TConfiguration, fill in all the configurations you want to
54// set across the stack, not only the ones you want to set in the immediate
55// TTransport/TProtocol.
56//
57// For example, say you want to migrate this old code into using TConfiguration:
58//
59// sccket := thrift.NewTSocketTimeout("host:port", time.Second)
60// transFactory := thrift.NewTFramedTransportFactoryMaxLength(
61// thrift.NewTTransportFactory(),
62// 1024 * 1024 * 256,
63// )
64// protoFactory := thrift.NewTBinaryProtocolFactory(true, true)
65//
66// This is the wrong way to do it because in the end the TConfiguration used by
67// socket and transFactory will be overwritten by the one used by protoFactory
68// because of TConfiguration propagation:
69//
70// // bad example, DO NOT USE
71// sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{
72// ConnectTimeout: time.Second,
73// SocketTimeout: time.Second,
74// })
75// transFactory := thrift.NewTFramedTransportFactoryConf(
76// thrift.NewTTransportFactory(),
77// &thrift.TConfiguration{
78// MaxFrameSize: 1024 * 1024 * 256,
79// },
80// )
81// protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{
82// TBinaryStrictRead: thrift.BoolPtr(true),
83// TBinaryStrictWrite: thrift.BoolPtr(true),
84// })
85//
86// This is the correct way to do it:
87//
88// conf := &thrift.TConfiguration{
89// ConnectTimeout: time.Second,
90// SocketTimeout: time.Second,
91//
92// MaxFrameSize: 1024 * 1024 * 256,
93//
94// TBinaryStrictRead: thrift.BoolPtr(true),
95// TBinaryStrictWrite: thrift.BoolPtr(true),
96// }
97// sccket := thrift.NewTSocketConf("host:port", conf)
98// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf)
99// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf)
100//
101// [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md
102type TConfiguration struct {
103 // If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead.
104 MaxMessageSize int32
105
106 // If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead.
107 //
108 // Also if MaxMessageSize < MaxFrameSize,
109 // MaxMessageSize will be used instead.
110 MaxFrameSize int32
111
112 // Connect and socket timeouts to be used by TSocket and TSSLSocket.
113 //
114 // 0 means no timeout.
115 //
116 // If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be
117 // used.
118 ConnectTimeout time.Duration
119 SocketTimeout time.Duration
120
121 // TLS config to be used by TSSLSocket.
122 TLSConfig *tls.Config
123
124 // Strict read/write configurations for TBinaryProtocol.
125 //
126 // BoolPtr helper function is available to use literal values.
127 TBinaryStrictRead *bool
128 TBinaryStrictWrite *bool
129
130 // The wrapped protocol id to be used in THeader transport/protocol.
131 //
132 // THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions
133 // are provided to help filling this value.
134 THeaderProtocolID *THeaderProtocolID
135
136 // Used internally by deprecated constructors, to avoid overriding
137 // underlying TTransport/TProtocol's cfg by accidental propagations.
138 //
139 // For external users this is always false.
140 noPropagation bool
141}
142
143// GetMaxMessageSize returns the max message size an implementation should
144// follow.
145//
146// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil.
147func (tc *TConfiguration) GetMaxMessageSize() int32 {
148 if tc == nil || tc.MaxMessageSize <= 0 {
149 return DEFAULT_MAX_MESSAGE_SIZE
150 }
151 return tc.MaxMessageSize
152}
153
154// GetMaxFrameSize returns the max frame size an implementation should follow.
155//
156// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil.
157//
158// If the configured max message size is smaller than the configured max frame
159// size, the smaller one will be returned instead.
160func (tc *TConfiguration) GetMaxFrameSize() int32 {
161 if tc == nil {
162 return DEFAULT_MAX_FRAME_SIZE
163 }
164 maxFrameSize := tc.MaxFrameSize
165 if maxFrameSize <= 0 {
166 maxFrameSize = DEFAULT_MAX_FRAME_SIZE
167 }
168 if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize {
169 return maxMessageSize
170 }
171 return maxFrameSize
172}
173
174// GetConnectTimeout returns the connect timeout should be used by TSocket and
175// TSSLSocket.
176//
177// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead.
178func (tc *TConfiguration) GetConnectTimeout() time.Duration {
179 if tc == nil || tc.ConnectTimeout < 0 {
180 return DEFAULT_CONNECT_TIMEOUT
181 }
182 return tc.ConnectTimeout
183}
184
185// GetSocketTimeout returns the socket timeout should be used by TSocket and
186// TSSLSocket.
187//
188// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead.
189func (tc *TConfiguration) GetSocketTimeout() time.Duration {
190 if tc == nil || tc.SocketTimeout < 0 {
191 return DEFAULT_SOCKET_TIMEOUT
192 }
193 return tc.SocketTimeout
194}
195
196// GetTLSConfig returns the tls config should be used by TSSLSocket.
197//
198// It's nil-safe. If tc is nil, nil will be returned instead.
199func (tc *TConfiguration) GetTLSConfig() *tls.Config {
200 if tc == nil {
201 return nil
202 }
203 return tc.TLSConfig
204}
205
206// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol
207// should follow.
208//
209// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or
210// tc.TBinaryStrictRead is nil.
211func (tc *TConfiguration) GetTBinaryStrictRead() bool {
212 if tc == nil || tc.TBinaryStrictRead == nil {
213 return DEFAULT_TBINARY_STRICT_READ
214 }
215 return *tc.TBinaryStrictRead
216}
217
218// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol
219// should follow.
220//
221// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or
222// tc.TBinaryStrictWrite is nil.
223func (tc *TConfiguration) GetTBinaryStrictWrite() bool {
224 if tc == nil || tc.TBinaryStrictWrite == nil {
225 return DEFAULT_TBINARY_STRICT_WRITE
226 }
227 return *tc.TBinaryStrictWrite
228}
229
230// GetTHeaderProtocolID returns the THeaderProtocolID should be used by
231// THeaderProtocol clients (for servers, they always use the same one as the
232// client instead).
233//
234// It's nil-safe. If either tc or tc.THeaderProtocolID is nil,
235// THeaderProtocolDefault will be returned instead.
236// THeaderProtocolDefault will also be returned if configured value is invalid.
237func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID {
238 if tc == nil || tc.THeaderProtocolID == nil {
239 return THeaderProtocolDefault
240 }
241 protoID := *tc.THeaderProtocolID
242 if err := protoID.Validate(); err != nil {
243 return THeaderProtocolDefault
244 }
245 return protoID
246}
247
248// THeaderProtocolIDPtr validates and returns the pointer to id.
249//
250// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault
251// and the validation error will be returned.
252func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) {
253 err := id.Validate()
254 if err != nil {
255 id = THeaderProtocolDefault
256 }
257 return &id, err
258}
259
260// THeaderProtocolIDPtrMust validates and returns the pointer to id.
261//
262// It's similar to THeaderProtocolIDPtr, but it panics on validation errors
263// instead of returning them.
264func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID {
265 ptr, err := THeaderProtocolIDPtr(id)
266 if err != nil {
267 panic(err)
268 }
269 return ptr
270}
271
272// TConfigurationSetter is an optional interface TProtocol, TTransport,
273// TProtocolFactory, TTransportFactory, and other implementations can implement.
274//
275// It's intended to be called during intializations.
276// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the
277// middle of a message is undefined:
278// It may or may not change the behavior of the current processing message,
279// and it may even cause the current message to fail.
280//
281// Note for implementations: SetTConfiguration might be called multiple times
282// with the same value in quick successions due to the implementation of the
283// propagation. Implementations should make SetTConfiguration as simple as
284// possible (usually just overwrite the stored configuration and propagate it to
285// the wrapped TTransports/TProtocols).
286type TConfigurationSetter interface {
287 SetTConfiguration(*TConfiguration)
288}
289
290// PropagateTConfiguration propagates cfg to impl if impl implements
291// TConfigurationSetter and cfg is non-nil, otherwise it does nothing.
292//
293// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration
294// with everything being default value, use &TConfiguration{} explicitly instead.
295func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) {
296 if cfg == nil || cfg.noPropagation {
297 return
298 }
299
300 if setter, ok := impl.(TConfigurationSetter); ok {
301 setter.SetTConfiguration(cfg)
302 }
303}
304
305func checkSizeForProtocol(size int32, cfg *TConfiguration) error {
306 if size < 0 {
307 return NewTProtocolExceptionWithType(
308 NEGATIVE_SIZE,
309 fmt.Errorf("negative size: %d", size),
310 )
311 }
312 if size > cfg.GetMaxMessageSize() {
313 return NewTProtocolExceptionWithType(
314 SIZE_LIMIT,
315 fmt.Errorf("size exceeded max allowed: %d", size),
316 )
317 }
318 return nil
319}
320
321type tTransportFactoryConf struct {
322 delegate TTransportFactory
323 cfg *TConfiguration
324}
325
326func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) {
327 trans, err := f.delegate.GetTransport(orig)
328 if err == nil {
329 PropagateTConfiguration(orig, f.cfg)
330 PropagateTConfiguration(trans, f.cfg)
331 }
332 return trans, err
333}
334
335func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) {
336 PropagateTConfiguration(f.delegate, f.cfg)
337 f.cfg = cfg
338}
339
340// TTransportFactoryConf wraps a TTransportFactory to propagate
341// TConfiguration on the factory's GetTransport calls.
342func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory {
343 return &tTransportFactoryConf{
344 delegate: delegate,
345 cfg: conf,
346 }
347}
348
349type tProtocolFactoryConf struct {
350 delegate TProtocolFactory
351 cfg *TConfiguration
352}
353
354func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol {
355 proto := f.delegate.GetProtocol(trans)
356 PropagateTConfiguration(trans, f.cfg)
357 PropagateTConfiguration(proto, f.cfg)
358 return proto
359}
360
361func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) {
362 PropagateTConfiguration(f.delegate, f.cfg)
363 f.cfg = cfg
364}
365
366// TProtocolFactoryConf wraps a TProtocolFactory to propagate
367// TConfiguration on the factory's GetProtocol calls.
368func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory {
369 return &tProtocolFactoryConf{
370 delegate: delegate,
371 cfg: conf,
372 }
373}
374
375var (
376 _ TConfigurationSetter = (*tTransportFactoryConf)(nil)
377 _ TConfigurationSetter = (*tProtocolFactoryConf)(nil)
378)