blob: a9565d399df03c51ce35b9ba96d9b74c8e46bdd0 [file] [log] [blame]
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -08001/*
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//
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -070059// socket, err := thrift.NewTSocketTimeout("host:port", time.Second, time.Second)
60// transFactory := thrift.NewTFramedTransportFactoryMaxLength(
61// thrift.NewTTransportFactory(),
62// 1024 * 1024 * 256,
63// )
64// protoFactory := thrift.NewTBinaryProtocolFactory(true, true)
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080065//
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//
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -070070// // bad example, DO NOT USE
71// socket := 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// })
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080085//
86// This is the correct way to do it:
87//
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -070088// conf := &thrift.TConfiguration{
89// ConnectTimeout: time.Second,
90// SocketTimeout: time.Second,
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080091//
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -070092// MaxFrameSize: 1024 * 1024 * 256,
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080093//
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -070094// TBinaryStrictRead: thrift.BoolPtr(true),
95// TBinaryStrictWrite: thrift.BoolPtr(true),
96// }
97// socket := thrift.NewTSocketConf("host:port", conf)
98// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf)
99// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf)
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800100//
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
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -0700135 // The write transforms to be applied to THeaderTransport.
136 THeaderTransforms []THeaderTransformID
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800137
138 // Used internally by deprecated constructors, to avoid overriding
139 // underlying TTransport/TProtocol's cfg by accidental propagations.
140 //
141 // For external users this is always false.
142 noPropagation bool
143}
144
145// GetMaxMessageSize returns the max message size an implementation should
146// follow.
147//
148// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil.
149func (tc *TConfiguration) GetMaxMessageSize() int32 {
150 if tc == nil || tc.MaxMessageSize <= 0 {
151 return DEFAULT_MAX_MESSAGE_SIZE
152 }
153 return tc.MaxMessageSize
154}
155
156// GetMaxFrameSize returns the max frame size an implementation should follow.
157//
158// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil.
159//
160// If the configured max message size is smaller than the configured max frame
161// size, the smaller one will be returned instead.
162func (tc *TConfiguration) GetMaxFrameSize() int32 {
163 if tc == nil {
164 return DEFAULT_MAX_FRAME_SIZE
165 }
166 maxFrameSize := tc.MaxFrameSize
167 if maxFrameSize <= 0 {
168 maxFrameSize = DEFAULT_MAX_FRAME_SIZE
169 }
170 if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize {
171 return maxMessageSize
172 }
173 return maxFrameSize
174}
175
176// GetConnectTimeout returns the connect timeout should be used by TSocket and
177// TSSLSocket.
178//
179// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead.
180func (tc *TConfiguration) GetConnectTimeout() time.Duration {
181 if tc == nil || tc.ConnectTimeout < 0 {
182 return DEFAULT_CONNECT_TIMEOUT
183 }
184 return tc.ConnectTimeout
185}
186
187// GetSocketTimeout returns the socket timeout should be used by TSocket and
188// TSSLSocket.
189//
190// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead.
191func (tc *TConfiguration) GetSocketTimeout() time.Duration {
192 if tc == nil || tc.SocketTimeout < 0 {
193 return DEFAULT_SOCKET_TIMEOUT
194 }
195 return tc.SocketTimeout
196}
197
198// GetTLSConfig returns the tls config should be used by TSSLSocket.
199//
200// It's nil-safe. If tc is nil, nil will be returned instead.
201func (tc *TConfiguration) GetTLSConfig() *tls.Config {
202 if tc == nil {
203 return nil
204 }
205 return tc.TLSConfig
206}
207
208// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol
209// should follow.
210//
211// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or
212// tc.TBinaryStrictRead is nil.
213func (tc *TConfiguration) GetTBinaryStrictRead() bool {
214 if tc == nil || tc.TBinaryStrictRead == nil {
215 return DEFAULT_TBINARY_STRICT_READ
216 }
217 return *tc.TBinaryStrictRead
218}
219
220// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol
221// should follow.
222//
223// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or
224// tc.TBinaryStrictWrite is nil.
225func (tc *TConfiguration) GetTBinaryStrictWrite() bool {
226 if tc == nil || tc.TBinaryStrictWrite == nil {
227 return DEFAULT_TBINARY_STRICT_WRITE
228 }
229 return *tc.TBinaryStrictWrite
230}
231
232// GetTHeaderProtocolID returns the THeaderProtocolID should be used by
233// THeaderProtocol clients (for servers, they always use the same one as the
234// client instead).
235//
236// It's nil-safe. If either tc or tc.THeaderProtocolID is nil,
237// THeaderProtocolDefault will be returned instead.
238// THeaderProtocolDefault will also be returned if configured value is invalid.
239func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID {
240 if tc == nil || tc.THeaderProtocolID == nil {
241 return THeaderProtocolDefault
242 }
243 protoID := *tc.THeaderProtocolID
244 if err := protoID.Validate(); err != nil {
245 return THeaderProtocolDefault
246 }
247 return protoID
248}
249
Yuxuan 'fishy' Wangb20f6752024-05-02 16:50:08 -0700250// GetTHeaderTransforms returns the THeaderTransformIDs to be applied on
251// THeaderTransport writing.
252//
253// It's nil-safe. If tc is nil, empty slice will be returned (meaning no
254// transforms to be applied).
255func (tc *TConfiguration) GetTHeaderTransforms() []THeaderTransformID {
256 if tc == nil {
257 return nil
258 }
259 return tc.THeaderTransforms
260}
261
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800262// THeaderProtocolIDPtr validates and returns the pointer to id.
263//
264// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault
265// and the validation error will be returned.
266func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) {
267 err := id.Validate()
268 if err != nil {
269 id = THeaderProtocolDefault
270 }
271 return &id, err
272}
273
274// THeaderProtocolIDPtrMust validates and returns the pointer to id.
275//
276// It's similar to THeaderProtocolIDPtr, but it panics on validation errors
277// instead of returning them.
278func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID {
279 ptr, err := THeaderProtocolIDPtr(id)
280 if err != nil {
281 panic(err)
282 }
283 return ptr
284}
285
286// TConfigurationSetter is an optional interface TProtocol, TTransport,
287// TProtocolFactory, TTransportFactory, and other implementations can implement.
288//
289// It's intended to be called during intializations.
290// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the
291// middle of a message is undefined:
292// It may or may not change the behavior of the current processing message,
293// and it may even cause the current message to fail.
294//
295// Note for implementations: SetTConfiguration might be called multiple times
296// with the same value in quick successions due to the implementation of the
297// propagation. Implementations should make SetTConfiguration as simple as
298// possible (usually just overwrite the stored configuration and propagate it to
299// the wrapped TTransports/TProtocols).
300type TConfigurationSetter interface {
301 SetTConfiguration(*TConfiguration)
302}
303
304// PropagateTConfiguration propagates cfg to impl if impl implements
305// TConfigurationSetter and cfg is non-nil, otherwise it does nothing.
306//
307// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration
308// with everything being default value, use &TConfiguration{} explicitly instead.
309func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) {
310 if cfg == nil || cfg.noPropagation {
311 return
312 }
313
314 if setter, ok := impl.(TConfigurationSetter); ok {
315 setter.SetTConfiguration(cfg)
316 }
317}
318
319func checkSizeForProtocol(size int32, cfg *TConfiguration) error {
320 if size < 0 {
321 return NewTProtocolExceptionWithType(
322 NEGATIVE_SIZE,
323 fmt.Errorf("negative size: %d", size),
324 )
325 }
326 if size > cfg.GetMaxMessageSize() {
327 return NewTProtocolExceptionWithType(
328 SIZE_LIMIT,
329 fmt.Errorf("size exceeded max allowed: %d", size),
330 )
331 }
332 return nil
333}
334
335type tTransportFactoryConf struct {
336 delegate TTransportFactory
337 cfg *TConfiguration
338}
339
340func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) {
341 trans, err := f.delegate.GetTransport(orig)
342 if err == nil {
343 PropagateTConfiguration(orig, f.cfg)
344 PropagateTConfiguration(trans, f.cfg)
345 }
346 return trans, err
347}
348
349func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) {
350 PropagateTConfiguration(f.delegate, f.cfg)
351 f.cfg = cfg
352}
353
354// TTransportFactoryConf wraps a TTransportFactory to propagate
355// TConfiguration on the factory's GetTransport calls.
356func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory {
357 return &tTransportFactoryConf{
358 delegate: delegate,
359 cfg: conf,
360 }
361}
362
363type tProtocolFactoryConf struct {
364 delegate TProtocolFactory
365 cfg *TConfiguration
366}
367
368func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol {
369 proto := f.delegate.GetProtocol(trans)
370 PropagateTConfiguration(trans, f.cfg)
371 PropagateTConfiguration(proto, f.cfg)
372 return proto
373}
374
375func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) {
376 PropagateTConfiguration(f.delegate, f.cfg)
377 f.cfg = cfg
378}
379
380// TProtocolFactoryConf wraps a TProtocolFactory to propagate
381// TConfiguration on the factory's GetProtocol calls.
382func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory {
383 return &tProtocolFactoryConf{
384 delegate: delegate,
385 cfg: conf,
386 }
387}
388
389var (
390 _ TConfigurationSetter = (*tTransportFactoryConf)(nil)
391 _ TConfigurationSetter = (*tProtocolFactoryConf)(nil)
392)