379 lines
12 KiB
Go
379 lines
12 KiB
Go
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
package thrift
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// Default TConfiguration values.
|
|
const (
|
|
DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024
|
|
DEFAULT_MAX_FRAME_SIZE = 16384000
|
|
|
|
DEFAULT_TBINARY_STRICT_READ = false
|
|
DEFAULT_TBINARY_STRICT_WRITE = true
|
|
|
|
DEFAULT_CONNECT_TIMEOUT = 0
|
|
DEFAULT_SOCKET_TIMEOUT = 0
|
|
)
|
|
|
|
// TConfiguration defines some configurations shared between TTransport,
|
|
// TProtocol, TTransportFactory, TProtocolFactory, and other implementations.
|
|
//
|
|
// When constructing TConfiguration, you only need to specify the non-default
|
|
// fields. All zero values have sane default values.
|
|
//
|
|
// Not all configurations defined are applicable to all implementations.
|
|
// Implementations are free to ignore the configurations not applicable to them.
|
|
//
|
|
// All functions attached to this type are nil-safe.
|
|
//
|
|
// See [1] for spec.
|
|
//
|
|
// NOTE: When using TConfiguration, fill in all the configurations you want to
|
|
// set across the stack, not only the ones you want to set in the immediate
|
|
// TTransport/TProtocol.
|
|
//
|
|
// For example, say you want to migrate this old code into using TConfiguration:
|
|
//
|
|
// sccket := thrift.NewTSocketTimeout("host:port", time.Second)
|
|
// transFactory := thrift.NewTFramedTransportFactoryMaxLength(
|
|
// thrift.NewTTransportFactory(),
|
|
// 1024 * 1024 * 256,
|
|
// )
|
|
// protoFactory := thrift.NewTBinaryProtocolFactory(true, true)
|
|
//
|
|
// This is the wrong way to do it because in the end the TConfiguration used by
|
|
// socket and transFactory will be overwritten by the one used by protoFactory
|
|
// because of TConfiguration propagation:
|
|
//
|
|
// // bad example, DO NOT USE
|
|
// sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{
|
|
// ConnectTimeout: time.Second,
|
|
// SocketTimeout: time.Second,
|
|
// })
|
|
// transFactory := thrift.NewTFramedTransportFactoryConf(
|
|
// thrift.NewTTransportFactory(),
|
|
// &thrift.TConfiguration{
|
|
// MaxFrameSize: 1024 * 1024 * 256,
|
|
// },
|
|
// )
|
|
// protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{
|
|
// TBinaryStrictRead: thrift.BoolPtr(true),
|
|
// TBinaryStrictWrite: thrift.BoolPtr(true),
|
|
// })
|
|
//
|
|
// This is the correct way to do it:
|
|
//
|
|
// conf := &thrift.TConfiguration{
|
|
// ConnectTimeout: time.Second,
|
|
// SocketTimeout: time.Second,
|
|
//
|
|
// MaxFrameSize: 1024 * 1024 * 256,
|
|
//
|
|
// TBinaryStrictRead: thrift.BoolPtr(true),
|
|
// TBinaryStrictWrite: thrift.BoolPtr(true),
|
|
// }
|
|
// sccket := thrift.NewTSocketConf("host:port", conf)
|
|
// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf)
|
|
// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf)
|
|
//
|
|
// [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md
|
|
type TConfiguration struct {
|
|
// If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead.
|
|
MaxMessageSize int32
|
|
|
|
// If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead.
|
|
//
|
|
// Also if MaxMessageSize < MaxFrameSize,
|
|
// MaxMessageSize will be used instead.
|
|
MaxFrameSize int32
|
|
|
|
// Connect and socket timeouts to be used by TSocket and TSSLSocket.
|
|
//
|
|
// 0 means no timeout.
|
|
//
|
|
// If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be
|
|
// used.
|
|
ConnectTimeout time.Duration
|
|
SocketTimeout time.Duration
|
|
|
|
// TLS config to be used by TSSLSocket.
|
|
TLSConfig *tls.Config
|
|
|
|
// Strict read/write configurations for TBinaryProtocol.
|
|
//
|
|
// BoolPtr helper function is available to use literal values.
|
|
TBinaryStrictRead *bool
|
|
TBinaryStrictWrite *bool
|
|
|
|
// The wrapped protocol id to be used in THeader transport/protocol.
|
|
//
|
|
// THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions
|
|
// are provided to help filling this value.
|
|
THeaderProtocolID *THeaderProtocolID
|
|
|
|
// Used internally by deprecated constructors, to avoid overriding
|
|
// underlying TTransport/TProtocol's cfg by accidental propagations.
|
|
//
|
|
// For external users this is always false.
|
|
noPropagation bool
|
|
}
|
|
|
|
// GetMaxMessageSize returns the max message size an implementation should
|
|
// follow.
|
|
//
|
|
// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil.
|
|
func (tc *TConfiguration) GetMaxMessageSize() int32 {
|
|
if tc == nil || tc.MaxMessageSize <= 0 {
|
|
return DEFAULT_MAX_MESSAGE_SIZE
|
|
}
|
|
return tc.MaxMessageSize
|
|
}
|
|
|
|
// GetMaxFrameSize returns the max frame size an implementation should follow.
|
|
//
|
|
// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil.
|
|
//
|
|
// If the configured max message size is smaller than the configured max frame
|
|
// size, the smaller one will be returned instead.
|
|
func (tc *TConfiguration) GetMaxFrameSize() int32 {
|
|
if tc == nil {
|
|
return DEFAULT_MAX_FRAME_SIZE
|
|
}
|
|
maxFrameSize := tc.MaxFrameSize
|
|
if maxFrameSize <= 0 {
|
|
maxFrameSize = DEFAULT_MAX_FRAME_SIZE
|
|
}
|
|
if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize {
|
|
return maxMessageSize
|
|
}
|
|
return maxFrameSize
|
|
}
|
|
|
|
// GetConnectTimeout returns the connect timeout should be used by TSocket and
|
|
// TSSLSocket.
|
|
//
|
|
// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead.
|
|
func (tc *TConfiguration) GetConnectTimeout() time.Duration {
|
|
if tc == nil || tc.ConnectTimeout < 0 {
|
|
return DEFAULT_CONNECT_TIMEOUT
|
|
}
|
|
return tc.ConnectTimeout
|
|
}
|
|
|
|
// GetSocketTimeout returns the socket timeout should be used by TSocket and
|
|
// TSSLSocket.
|
|
//
|
|
// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead.
|
|
func (tc *TConfiguration) GetSocketTimeout() time.Duration {
|
|
if tc == nil || tc.SocketTimeout < 0 {
|
|
return DEFAULT_SOCKET_TIMEOUT
|
|
}
|
|
return tc.SocketTimeout
|
|
}
|
|
|
|
// GetTLSConfig returns the tls config should be used by TSSLSocket.
|
|
//
|
|
// It's nil-safe. If tc is nil, nil will be returned instead.
|
|
func (tc *TConfiguration) GetTLSConfig() *tls.Config {
|
|
if tc == nil {
|
|
return nil
|
|
}
|
|
return tc.TLSConfig
|
|
}
|
|
|
|
// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol
|
|
// should follow.
|
|
//
|
|
// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or
|
|
// tc.TBinaryStrictRead is nil.
|
|
func (tc *TConfiguration) GetTBinaryStrictRead() bool {
|
|
if tc == nil || tc.TBinaryStrictRead == nil {
|
|
return DEFAULT_TBINARY_STRICT_READ
|
|
}
|
|
return *tc.TBinaryStrictRead
|
|
}
|
|
|
|
// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol
|
|
// should follow.
|
|
//
|
|
// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or
|
|
// tc.TBinaryStrictWrite is nil.
|
|
func (tc *TConfiguration) GetTBinaryStrictWrite() bool {
|
|
if tc == nil || tc.TBinaryStrictWrite == nil {
|
|
return DEFAULT_TBINARY_STRICT_WRITE
|
|
}
|
|
return *tc.TBinaryStrictWrite
|
|
}
|
|
|
|
// GetTHeaderProtocolID returns the THeaderProtocolID should be used by
|
|
// THeaderProtocol clients (for servers, they always use the same one as the
|
|
// client instead).
|
|
//
|
|
// It's nil-safe. If either tc or tc.THeaderProtocolID is nil,
|
|
// THeaderProtocolDefault will be returned instead.
|
|
// THeaderProtocolDefault will also be returned if configured value is invalid.
|
|
func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID {
|
|
if tc == nil || tc.THeaderProtocolID == nil {
|
|
return THeaderProtocolDefault
|
|
}
|
|
protoID := *tc.THeaderProtocolID
|
|
if err := protoID.Validate(); err != nil {
|
|
return THeaderProtocolDefault
|
|
}
|
|
return protoID
|
|
}
|
|
|
|
// THeaderProtocolIDPtr validates and returns the pointer to id.
|
|
//
|
|
// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault
|
|
// and the validation error will be returned.
|
|
func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) {
|
|
err := id.Validate()
|
|
if err != nil {
|
|
id = THeaderProtocolDefault
|
|
}
|
|
return &id, err
|
|
}
|
|
|
|
// THeaderProtocolIDPtrMust validates and returns the pointer to id.
|
|
//
|
|
// It's similar to THeaderProtocolIDPtr, but it panics on validation errors
|
|
// instead of returning them.
|
|
func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID {
|
|
ptr, err := THeaderProtocolIDPtr(id)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ptr
|
|
}
|
|
|
|
// TConfigurationSetter is an optional interface TProtocol, TTransport,
|
|
// TProtocolFactory, TTransportFactory, and other implementations can implement.
|
|
//
|
|
// It's intended to be called during intializations.
|
|
// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the
|
|
// middle of a message is undefined:
|
|
// It may or may not change the behavior of the current processing message,
|
|
// and it may even cause the current message to fail.
|
|
//
|
|
// Note for implementations: SetTConfiguration might be called multiple times
|
|
// with the same value in quick successions due to the implementation of the
|
|
// propagation. Implementations should make SetTConfiguration as simple as
|
|
// possible (usually just overwrite the stored configuration and propagate it to
|
|
// the wrapped TTransports/TProtocols).
|
|
type TConfigurationSetter interface {
|
|
SetTConfiguration(*TConfiguration)
|
|
}
|
|
|
|
// PropagateTConfiguration propagates cfg to impl if impl implements
|
|
// TConfigurationSetter and cfg is non-nil, otherwise it does nothing.
|
|
//
|
|
// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration
|
|
// with everything being default value, use &TConfiguration{} explicitly instead.
|
|
func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) {
|
|
if cfg == nil || cfg.noPropagation {
|
|
return
|
|
}
|
|
|
|
if setter, ok := impl.(TConfigurationSetter); ok {
|
|
setter.SetTConfiguration(cfg)
|
|
}
|
|
}
|
|
|
|
func checkSizeForProtocol(size int32, cfg *TConfiguration) error {
|
|
if size < 0 {
|
|
return NewTProtocolExceptionWithType(
|
|
NEGATIVE_SIZE,
|
|
fmt.Errorf("negative size: %d", size),
|
|
)
|
|
}
|
|
if size > cfg.GetMaxMessageSize() {
|
|
return NewTProtocolExceptionWithType(
|
|
SIZE_LIMIT,
|
|
fmt.Errorf("size exceeded max allowed: %d", size),
|
|
)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type tTransportFactoryConf struct {
|
|
delegate TTransportFactory
|
|
cfg *TConfiguration
|
|
}
|
|
|
|
func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) {
|
|
trans, err := f.delegate.GetTransport(orig)
|
|
if err == nil {
|
|
PropagateTConfiguration(orig, f.cfg)
|
|
PropagateTConfiguration(trans, f.cfg)
|
|
}
|
|
return trans, err
|
|
}
|
|
|
|
func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) {
|
|
PropagateTConfiguration(f.delegate, f.cfg)
|
|
f.cfg = cfg
|
|
}
|
|
|
|
// TTransportFactoryConf wraps a TTransportFactory to propagate
|
|
// TConfiguration on the factory's GetTransport calls.
|
|
func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory {
|
|
return &tTransportFactoryConf{
|
|
delegate: delegate,
|
|
cfg: conf,
|
|
}
|
|
}
|
|
|
|
type tProtocolFactoryConf struct {
|
|
delegate TProtocolFactory
|
|
cfg *TConfiguration
|
|
}
|
|
|
|
func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol {
|
|
proto := f.delegate.GetProtocol(trans)
|
|
PropagateTConfiguration(trans, f.cfg)
|
|
PropagateTConfiguration(proto, f.cfg)
|
|
return proto
|
|
}
|
|
|
|
func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) {
|
|
PropagateTConfiguration(f.delegate, f.cfg)
|
|
f.cfg = cfg
|
|
}
|
|
|
|
// TProtocolFactoryConf wraps a TProtocolFactory to propagate
|
|
// TConfiguration on the factory's GetProtocol calls.
|
|
func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory {
|
|
return &tProtocolFactoryConf{
|
|
delegate: delegate,
|
|
cfg: conf,
|
|
}
|
|
}
|
|
|
|
var (
|
|
_ TConfigurationSetter = (*tTransportFactoryConf)(nil)
|
|
_ TConfigurationSetter = (*tProtocolFactoryConf)(nil)
|
|
)
|