237 lines
7.8 KiB
Go
237 lines
7.8 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 (
|
|
"fmt"
|
|
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
const (
|
|
// Message id for protocol level errors
|
|
invalidMessageID uint32 = 0xFFFFFFFF
|
|
)
|
|
|
|
// A SystemErrCode indicates how a caller should handle a system error returned from a peer
|
|
type SystemErrCode byte
|
|
|
|
//go:generate stringer -type=SystemErrCode
|
|
|
|
const (
|
|
// ErrCodeInvalid is an invalid error code, and should not be used
|
|
ErrCodeInvalid SystemErrCode = 0x00
|
|
|
|
// ErrCodeTimeout indicates the peer timed out. Callers can retry the request
|
|
// on another peer if the request is safe to retry.
|
|
ErrCodeTimeout SystemErrCode = 0x01
|
|
|
|
// ErrCodeCancelled indicates that the request was cancelled on the peer. Callers
|
|
// can retry the request on the same or another peer if the request is safe to retry
|
|
ErrCodeCancelled SystemErrCode = 0x02
|
|
|
|
// ErrCodeBusy indicates that the request was not dispatched because the peer
|
|
// was too busy to handle it. Callers can retry the request on another peer, and should
|
|
// reweight their connections to direct less traffic to this peer until it recovers.
|
|
ErrCodeBusy SystemErrCode = 0x03
|
|
|
|
// ErrCodeDeclined indicates that the request not dispatched because the peer
|
|
// declined to handle it, typically because the peer is not yet ready to handle it.
|
|
// Callers can retry the request on another peer, but should not reweight their connections
|
|
// and should continue to send traffic to this peer.
|
|
ErrCodeDeclined SystemErrCode = 0x04
|
|
|
|
// ErrCodeUnexpected indicates that the request failed for an unexpected reason, typically
|
|
// a crash or other unexpected handling. The request may have been processed before the failure;
|
|
// callers should retry the request on this or another peer only if the request is safe to retry
|
|
ErrCodeUnexpected SystemErrCode = 0x05
|
|
|
|
// ErrCodeBadRequest indicates that the request was malformed, and could not be processed.
|
|
// Callers should not bother to retry the request, as there is no chance it will be handled.
|
|
ErrCodeBadRequest SystemErrCode = 0x06
|
|
|
|
// ErrCodeNetwork indicates a network level error, such as a connection reset.
|
|
// Callers can retry the request if the request is safe to retry
|
|
ErrCodeNetwork SystemErrCode = 0x07
|
|
|
|
// ErrCodeProtocol indincates a fatal protocol error communicating with the peer. The connection
|
|
// will be terminated.
|
|
ErrCodeProtocol SystemErrCode = 0xFF
|
|
)
|
|
|
|
var (
|
|
// ErrServerBusy is a SystemError indicating the server is busy
|
|
ErrServerBusy = NewSystemError(ErrCodeBusy, "server busy")
|
|
|
|
// ErrRequestCancelled is a SystemError indicating the request has been cancelled on the peer
|
|
ErrRequestCancelled = NewSystemError(ErrCodeCancelled, "request cancelled")
|
|
|
|
// ErrTimeout is a SytemError indicating the request has timed out
|
|
ErrTimeout = NewSystemError(ErrCodeTimeout, "timeout")
|
|
|
|
// ErrTimeoutRequired is a SystemError indicating that timeouts must be specified.
|
|
ErrTimeoutRequired = NewSystemError(ErrCodeBadRequest, "timeout required")
|
|
|
|
// ErrChannelClosed is a SystemError indicating that the channel has been closed.
|
|
ErrChannelClosed = NewSystemError(ErrCodeDeclined, "closed channel")
|
|
|
|
// ErrMethodTooLarge is a SystemError indicating that the method is too large.
|
|
ErrMethodTooLarge = NewSystemError(ErrCodeProtocol, "method too large")
|
|
)
|
|
|
|
// MetricsKey is a string representation of the error code that's suitable for
|
|
// inclusion in metrics tags.
|
|
func (c SystemErrCode) MetricsKey() string {
|
|
switch c {
|
|
case ErrCodeInvalid:
|
|
// Shouldn't ever need this.
|
|
return "invalid"
|
|
case ErrCodeTimeout:
|
|
return "timeout"
|
|
case ErrCodeCancelled:
|
|
return "cancelled"
|
|
case ErrCodeBusy:
|
|
return "busy"
|
|
case ErrCodeDeclined:
|
|
return "declined"
|
|
case ErrCodeUnexpected:
|
|
return "unexpected-error"
|
|
case ErrCodeBadRequest:
|
|
return "bad-request"
|
|
case ErrCodeNetwork:
|
|
return "network-error"
|
|
case ErrCodeProtocol:
|
|
return "protocol-error"
|
|
default:
|
|
return c.String()
|
|
}
|
|
}
|
|
|
|
func (c SystemErrCode) relayMetricsKey() string {
|
|
switch c {
|
|
case ErrCodeInvalid:
|
|
return "relay-invalid"
|
|
case ErrCodeTimeout:
|
|
return "relay-timeout"
|
|
case ErrCodeCancelled:
|
|
return "relay-cancelled"
|
|
case ErrCodeBusy:
|
|
return "relay-busy"
|
|
case ErrCodeDeclined:
|
|
return "relay-declined"
|
|
case ErrCodeUnexpected:
|
|
return "relay-unexpected-error"
|
|
case ErrCodeBadRequest:
|
|
return "relay-bad-request"
|
|
case ErrCodeNetwork:
|
|
return "relay-network-error"
|
|
case ErrCodeProtocol:
|
|
return "relay-protocol-error"
|
|
default:
|
|
return "relay-" + c.String()
|
|
}
|
|
}
|
|
|
|
// A SystemError is a system-level error, containing an error code and message
|
|
// TODO(mmihic): Probably we want to hide this interface, and let application code
|
|
// just deal with standard raw errors.
|
|
type SystemError struct {
|
|
code SystemErrCode
|
|
msg string
|
|
wrapped error
|
|
}
|
|
|
|
// NewSystemError defines a new SystemError with a code and message
|
|
func NewSystemError(code SystemErrCode, msg string, args ...interface{}) error {
|
|
return SystemError{code: code, msg: fmt.Sprintf(msg, args...)}
|
|
}
|
|
|
|
// NewWrappedSystemError defines a new SystemError wrapping an existing error
|
|
func NewWrappedSystemError(code SystemErrCode, wrapped error) error {
|
|
if se, ok := wrapped.(SystemError); ok {
|
|
return se
|
|
}
|
|
|
|
return SystemError{code: code, msg: fmt.Sprint(wrapped), wrapped: wrapped}
|
|
}
|
|
|
|
// Error returns the code and message, conforming to the error interface
|
|
func (se SystemError) Error() string {
|
|
return fmt.Sprintf("tchannel error %v: %s", se.Code(), se.msg)
|
|
}
|
|
|
|
// Wrapped returns the wrapped error
|
|
func (se SystemError) Wrapped() error { return se.wrapped }
|
|
|
|
// Code returns the SystemError code, for sending to a peer
|
|
func (se SystemError) Code() SystemErrCode {
|
|
return se.code
|
|
}
|
|
|
|
// Message returns the SystemError message.
|
|
func (se SystemError) Message() string {
|
|
return se.msg
|
|
}
|
|
|
|
// GetContextError converts the context error to a tchannel error.
|
|
func GetContextError(err error) error {
|
|
if err == context.DeadlineExceeded {
|
|
return ErrTimeout
|
|
}
|
|
if err == context.Canceled {
|
|
return ErrRequestCancelled
|
|
}
|
|
return err
|
|
}
|
|
|
|
// GetSystemErrorCode returns the code to report for the given error. If the error is a
|
|
// SystemError, we can get the code directly. Otherwise treat it as an unexpected error
|
|
func GetSystemErrorCode(err error) SystemErrCode {
|
|
if err == nil {
|
|
return ErrCodeInvalid
|
|
}
|
|
|
|
if se, ok := err.(SystemError); ok {
|
|
return se.Code()
|
|
}
|
|
|
|
return ErrCodeUnexpected
|
|
}
|
|
|
|
// GetSystemErrorMessage returns the message to report for the given error. If the error is a
|
|
// SystemError, we can get the underlying message. Otherwise, use the Error() method.
|
|
func GetSystemErrorMessage(err error) string {
|
|
if se, ok := err.(SystemError); ok {
|
|
return se.Message()
|
|
}
|
|
|
|
return err.Error()
|
|
}
|
|
|
|
type errConnNotActive struct {
|
|
info string
|
|
state connectionState
|
|
}
|
|
|
|
func (e errConnNotActive) Error() string {
|
|
return fmt.Sprintf("%v connection is not active: %v", e.info, e.state)
|
|
}
|