199 lines
5.5 KiB
Go
199 lines
5.5 KiB
Go
|
// Copyright (c) 2015 Uber Technologies, Inc.
|
||
|
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
package tchannel
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
_callerNameKeyBytes = []byte(CallerName)
|
||
|
_routingDelegateKeyBytes = []byte(RoutingDelegate)
|
||
|
_routingKeyKeyBytes = []byte(RoutingKey)
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// Common to many frame types.
|
||
|
_flagsIndex = 0
|
||
|
|
||
|
// For call req.
|
||
|
_ttlIndex = 1
|
||
|
_ttlLen = 4
|
||
|
_spanIndex = _ttlIndex + _ttlLen
|
||
|
_spanLength = 25
|
||
|
_serviceLenIndex = _spanIndex + _spanLength
|
||
|
_serviceNameIndex = _serviceLenIndex + 1
|
||
|
|
||
|
// For call res and call res continue.
|
||
|
_resCodeOK = 0x00
|
||
|
_resCodeIndex = 1
|
||
|
|
||
|
// For error.
|
||
|
_errCodeIndex = 0
|
||
|
)
|
||
|
|
||
|
type lazyError struct {
|
||
|
*Frame
|
||
|
}
|
||
|
|
||
|
func newLazyError(f *Frame) lazyError {
|
||
|
if msgType := f.Header.messageType; msgType != messageTypeError {
|
||
|
panic(fmt.Errorf("newLazyError called for wrong messageType: %v", msgType))
|
||
|
}
|
||
|
return lazyError{f}
|
||
|
}
|
||
|
|
||
|
func (e lazyError) Code() SystemErrCode {
|
||
|
return SystemErrCode(e.Payload[_errCodeIndex])
|
||
|
}
|
||
|
|
||
|
type lazyCallRes struct {
|
||
|
*Frame
|
||
|
}
|
||
|
|
||
|
func newLazyCallRes(f *Frame) lazyCallRes {
|
||
|
if msgType := f.Header.messageType; msgType != messageTypeCallRes {
|
||
|
panic(fmt.Errorf("newLazyCallRes called for wrong messageType: %v", msgType))
|
||
|
}
|
||
|
return lazyCallRes{f}
|
||
|
}
|
||
|
|
||
|
func (cr lazyCallRes) OK() bool {
|
||
|
return cr.Payload[_resCodeIndex] == _resCodeOK
|
||
|
}
|
||
|
|
||
|
// TODO: Use []byte instead of string for caller/method to avoid allocations.
|
||
|
type lazyCallReq struct {
|
||
|
*Frame
|
||
|
|
||
|
caller, method, delegate, key []byte
|
||
|
}
|
||
|
|
||
|
// TODO: Consider pooling lazyCallReq and using pointers to the struct.
|
||
|
|
||
|
func newLazyCallReq(f *Frame) lazyCallReq {
|
||
|
if msgType := f.Header.messageType; msgType != messageTypeCallReq {
|
||
|
panic(fmt.Errorf("newLazyCallReq called for wrong messageType: %v", msgType))
|
||
|
}
|
||
|
|
||
|
cr := lazyCallReq{Frame: f}
|
||
|
|
||
|
serviceLen := f.Payload[_serviceLenIndex]
|
||
|
// nh:1 (hk~1 hv~1){nh}
|
||
|
headerStart := _serviceLenIndex + 1 /* length byte */ + serviceLen
|
||
|
numHeaders := int(f.Payload[headerStart])
|
||
|
cur := int(headerStart) + 1
|
||
|
for i := 0; i < numHeaders; i++ {
|
||
|
keyLen := int(f.Payload[cur])
|
||
|
cur++
|
||
|
key := f.Payload[cur : cur+keyLen]
|
||
|
cur += keyLen
|
||
|
|
||
|
valLen := int(f.Payload[cur])
|
||
|
cur++
|
||
|
val := f.Payload[cur : cur+valLen]
|
||
|
cur += valLen
|
||
|
|
||
|
if bytes.Equal(key, _callerNameKeyBytes) {
|
||
|
cr.caller = val
|
||
|
} else if bytes.Equal(key, _routingDelegateKeyBytes) {
|
||
|
cr.delegate = val
|
||
|
} else if bytes.Equal(key, _routingKeyKeyBytes) {
|
||
|
cr.key = val
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// csumtype:1 (csum:4){0,1} arg1~2 arg2~2 arg3~2
|
||
|
checkSumType := ChecksumType(f.Payload[cur])
|
||
|
cur += 1 /* checksum */ + checkSumType.ChecksumSize()
|
||
|
|
||
|
// arg1~2
|
||
|
arg1Len := int(binary.BigEndian.Uint16(f.Payload[cur : cur+2]))
|
||
|
cur += 2
|
||
|
cr.method = f.Payload[cur : cur+arg1Len]
|
||
|
return cr
|
||
|
}
|
||
|
|
||
|
// Caller returns the name of the originator of this callReq.
|
||
|
func (f lazyCallReq) Caller() []byte {
|
||
|
return f.caller
|
||
|
}
|
||
|
|
||
|
// Service returns the name of the destination service for this callReq.
|
||
|
func (f lazyCallReq) Service() []byte {
|
||
|
l := f.Payload[_serviceLenIndex]
|
||
|
return f.Payload[_serviceNameIndex : _serviceNameIndex+l]
|
||
|
}
|
||
|
|
||
|
// Method returns the name of the method being called.
|
||
|
func (f lazyCallReq) Method() []byte {
|
||
|
return f.method
|
||
|
}
|
||
|
|
||
|
// RoutingDelegate returns the routing delegate for this call req, if any.
|
||
|
func (f lazyCallReq) RoutingDelegate() []byte {
|
||
|
return f.delegate
|
||
|
}
|
||
|
|
||
|
// RoutingKey returns the routing delegate for this call req, if any.
|
||
|
func (f lazyCallReq) RoutingKey() []byte {
|
||
|
return f.key
|
||
|
}
|
||
|
|
||
|
// TTL returns the time to live for this callReq.
|
||
|
func (f lazyCallReq) TTL() time.Duration {
|
||
|
ttl := binary.BigEndian.Uint32(f.Payload[_ttlIndex : _ttlIndex+_ttlLen])
|
||
|
return time.Duration(ttl) * time.Millisecond
|
||
|
}
|
||
|
|
||
|
// SetTTL overwrites the frame's TTL.
|
||
|
func (f lazyCallReq) SetTTL(d time.Duration) {
|
||
|
ttl := uint32(d / time.Millisecond)
|
||
|
binary.BigEndian.PutUint32(f.Payload[_ttlIndex:_ttlIndex+_ttlLen], ttl)
|
||
|
}
|
||
|
|
||
|
// Span returns the Span
|
||
|
func (f lazyCallReq) Span() Span {
|
||
|
return callReqSpan(f.Frame)
|
||
|
}
|
||
|
|
||
|
// HasMoreFragments returns whether the callReq has more fragments.
|
||
|
func (f lazyCallReq) HasMoreFragments() bool {
|
||
|
return f.Payload[_flagsIndex]&hasMoreFragmentsFlag != 0
|
||
|
}
|
||
|
|
||
|
// finishesCall checks whether this frame is the last one we should expect for
|
||
|
// this RPC req-res.
|
||
|
func finishesCall(f *Frame) bool {
|
||
|
switch f.messageType() {
|
||
|
case messageTypeError:
|
||
|
return true
|
||
|
case messageTypeCallRes, messageTypeCallResContinue:
|
||
|
flags := f.Payload[_flagsIndex]
|
||
|
return flags&hasMoreFragmentsFlag == 0
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|