go.uber.org/yarpc@v1.72.1/transport/tchannel/error.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package tchannel
    22  
    23  import (
    24  	"context"
    25  	"errors"
    26  
    27  	"github.com/uber/tchannel-go"
    28  	"go.uber.org/yarpc/api/transport"
    29  	"go.uber.org/yarpc/yarpcerrors"
    30  )
    31  
    32  // GetResponseErrorMeta extracts the TChannel specific error response information
    33  // from an error. Returns nil if no information is available.
    34  //
    35  // This API is experimental and subject to change or break at anytime.
    36  func GetResponseErrorMeta(err error) *ResponseErrorMeta {
    37  	if err == nil {
    38  		return nil
    39  	}
    40  
    41  	var meta *ytchanError
    42  	if !errors.As(err, &meta) {
    43  		return nil
    44  	}
    45  	return &ResponseErrorMeta{
    46  		Code: meta.err.Code(),
    47  	}
    48  }
    49  
    50  // ResponseErrorMeta exposes TChannel specific information from an error.
    51  //
    52  // This API is experimental and subject to change or break at anytime.
    53  type ResponseErrorMeta struct {
    54  	Code tchannel.SystemErrCode
    55  }
    56  
    57  // private error for propagating transparently
    58  type ytchanError struct {
    59  	err tchannel.SystemError
    60  }
    61  
    62  func (y *ytchanError) Error() string { return y.err.Message() }
    63  
    64  func fromSystemError(err tchannel.SystemError) error {
    65  	code, ok := _tchannelCodeToCode[err.Code()]
    66  	if !ok {
    67  		return yarpcerrors.Newf(yarpcerrors.CodeInternal, "got tchannel.SystemError %v which did not have a matching YARPC code", err)
    68  	}
    69  
    70  	// transparently wrap our private error so we can extract it with
    71  	// GetResponseErrorMeta.
    72  	return yarpcerrors.Newf(code, "%w", &ytchanError{err: err})
    73  }
    74  
    75  func toYARPCError(req *transport.Request, err error) error {
    76  	if err == nil {
    77  		return err
    78  	}
    79  	if yarpcerrors.IsStatus(err) {
    80  		return err
    81  	}
    82  	if err, ok := err.(tchannel.SystemError); ok {
    83  		return fromSystemError(err)
    84  	}
    85  	if err == context.DeadlineExceeded {
    86  		return yarpcerrors.DeadlineExceededErrorf("deadline exceeded for service: %q, procedure: %q", req.Service, req.Procedure)
    87  	}
    88  	return yarpcerrors.UnknownErrorf("received unknown error calling service: %q, procedure: %q, err: %s", req.Service, req.Procedure, err.Error())
    89  }