diff options
Diffstat (limited to 'vendor/github.com/twitchtv/twirp/errors.go')
| -rw-r--r-- | vendor/github.com/twitchtv/twirp/errors.go | 428 |
1 files changed, 0 insertions, 428 deletions
diff --git a/vendor/github.com/twitchtv/twirp/errors.go b/vendor/github.com/twitchtv/twirp/errors.go deleted file mode 100644 index b9664b4..0000000 --- a/vendor/github.com/twitchtv/twirp/errors.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may not -// use this file except in compliance with the License. A copy of the License is -// located at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// or in the "license" file accompanying this file. This file 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 twirp provides core types used in generated Twirp servers and client. -// -// Twirp services handle errors using the `twirp.Error` interface. -// -// For example, a server method may return an InvalidArgumentError: -// -// if req.Order != "DESC" && req.Order != "ASC" { -// return nil, twirp.InvalidArgumentError("Order", "must be DESC or ASC") -// } -// -// And the same twirp.Error is returned by the client, for example: -// -// resp, err := twirpClient.RPCMethod(ctx, req) -// if err != nil { -// if twerr, ok := err.(twirp.Error); ok { -// switch twerr.Code() { -// case twirp.InvalidArgument: -// log.Error("invalid argument "+twirp.Meta("argument")) -// default: -// log.Error(twerr.Error()) -// } -// } -// } -// -// Clients may also return Internal errors if something failed on the system: -// the server, the network, or the client itself (i.e. failure parsing -// response). -// -package twirp - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "strconv" -) - -// Error represents an error in a Twirp service call. -type Error interface { - // Code is of the valid error codes. - Code() ErrorCode - - // Msg returns a human-readable, unstructured messages describing the error. - Msg() string - - // WithMeta returns a copy of the Error with the given key-value pair attached - // as metadata. If the key is already set, it is overwritten. - WithMeta(key string, val string) Error - - // Meta returns the stored value for the given key. If the key has no set - // value, Meta returns an empty string. There is no way to distinguish between - // an unset value and an explicit empty string. - Meta(key string) string - - // MetaMap returns the complete key-value metadata map stored on the error. - MetaMap() map[string]string - - // Error returns a string of the form "twirp error <Code>: <Msg>" - Error() string -} - -// code.Error(msg) builds a new Twirp error with code and msg. Example: -// twirp.NotFound.Error("Resource not found") -// twirp.Internal.Error("Oops") -func (code ErrorCode) Error(msg string) Error { - return NewError(code, msg) -} - -// code.Errorf(msg, args...) builds a new Twirp error with code and formatted msg. -// The format may include "%w" to wrap other errors. Examples: -// twirp.Internal.Error("Oops: %w", originalErr) -// twirp.NotFound.Error("Resource not found with id: %q", resourceID) -func (code ErrorCode) Errorf(msgFmt string, a ...interface{}) Error { - return NewErrorf(code, msgFmt, a...) -} - -// WrapError allows Twirp errors to wrap other errors. -// The wrapped error can be extracted later with (github.com/pkg/errors).Unwrap -// or errors.Is from the standard errors package on Go 1.13+. -func WrapError(twerr Error, err error) Error { - return &wrappedErr{ - wrapper: twerr, - cause: err, - } -} - -// NewError builds a twirp.Error. The code must be one of the valid predefined constants. -// To add metadata, use .WithMeta(key, value) method after building the error. -func NewError(code ErrorCode, msg string) Error { - if !IsValidErrorCode(code) { - return &twerr{code: Internal, msg: "invalid error type " + string(code)} - } - return &twerr{code: code, msg: msg} -} - -// NewErrorf builds a twirp.Error with a formatted msg. -// The format may include "%w" to wrap other errors. Examples: -// twirp.NewErrorf(twirp.Internal, "Oops: %w", originalErr) -// twirp.NewErrorf(twirp.NotFound, "resource with id: %q", resourceID) -func NewErrorf(code ErrorCode, msgFmt string, a ...interface{}) Error { - err := fmt.Errorf(msgFmt, a...) // format error message, may include "%w" with an original error - twerr := NewError(code, err.Error()) // use the error as msg - return WrapError(twerr, err) // wrap so the original error can be identified with errors.Is -} - -// NotFoundError is a convenience constructor for NotFound errors. -func NotFoundError(msg string) Error { - return NewError(NotFound, msg) -} - -// InvalidArgumentError is a convenience constructor for InvalidArgument errors. -// The argument name is included on the "argument" metadata for convenience. -func InvalidArgumentError(argument string, validationMsg string) Error { - err := NewError(InvalidArgument, argument+" "+validationMsg) - err = err.WithMeta("argument", argument) - return err -} - -// RequiredArgumentError builds an InvalidArgument error. -// Useful when a request argument is expected to have a non-zero value. -func RequiredArgumentError(argument string) Error { - return InvalidArgumentError(argument, "is required") -} - -// InternalError is a convenience constructor for Internal errors. -func InternalError(msg string) Error { - return NewError(Internal, msg) -} - -// InternalErrorf uses the formatted message as the internal error msg. -// The format may include "%w" to wrap other errors. Examples: -// twirp.InternalErrorf("database error: %w", err) -// twirp.InternalErrorf("failed to load resource %q: %w", resourceID, originalErr) -func InternalErrorf(msgFmt string, a ...interface{}) Error { - return NewErrorf(Internal, msgFmt, a...) -} - -// InternalErrorWith makes an internal error, wrapping the original error and using it -// for the error message, and with metadata "cause" with the original error type. -// This function is used by Twirp services to wrap non-Twirp errors as internal errors. -// The wrapped error can be extracted later with (github.com/pkg/errors).Unwrap -// or errors.Is from the standard errors package on Go 1.13+. -func InternalErrorWith(err error) Error { - twerr := NewError(Internal, err.Error()) - twerr = twerr.WithMeta("cause", fmt.Sprintf("%T", err)) // to easily tell apart wrapped internal errors from explicit ones - return WrapError(twerr, err) -} - -// ErrorCode represents a Twirp error type. -type ErrorCode string - -// Valid Twirp error types. Most error types are equivalent to gRPC status codes -// and follow the same semantics. -const ( - // Canceled indicates the operation was cancelled (typically by the caller). - Canceled ErrorCode = "canceled" - - // Unknown error. For example when handling errors raised by APIs that do not - // return enough error information. - Unknown ErrorCode = "unknown" - - // InvalidArgument indicates client specified an invalid argument. It - // indicates arguments that are problematic regardless of the state of the - // system (i.e. a malformed file name, required argument, number out of range, - // etc.). - InvalidArgument ErrorCode = "invalid_argument" - - // Malformed indicates an error occurred while decoding the client's request. - // This may mean that the message was encoded improperly, or that there is a - // disagreement in message format between the client and server. - Malformed ErrorCode = "malformed" - - // DeadlineExceeded means operation expired before completion. For operations - // that change the state of the system, this error may be returned even if the - // operation has completed successfully (timeout). - DeadlineExceeded ErrorCode = "deadline_exceeded" - - // NotFound means some requested entity was not found. - NotFound ErrorCode = "not_found" - - // BadRoute means that the requested URL path wasn't routable to a Twirp - // service and method. This is returned by the generated server, and usually - // shouldn't be returned by applications. Instead, applications should use - // NotFound or Unimplemented. - BadRoute ErrorCode = "bad_route" - - // AlreadyExists means an attempt to create an entity failed because one - // already exists. - AlreadyExists ErrorCode = "already_exists" - - // PermissionDenied indicates the caller does not have permission to execute - // the specified operation. It must not be used if the caller cannot be - // identified (Unauthenticated). - PermissionDenied ErrorCode = "permission_denied" - - // Unauthenticated indicates the request does not have valid authentication - // credentials for the operation. - Unauthenticated ErrorCode = "unauthenticated" - - // ResourceExhausted indicates some resource has been exhausted or rate-limited, - // perhaps a per-user quota, or perhaps the entire file system is out of space. - ResourceExhausted ErrorCode = "resource_exhausted" - - // FailedPrecondition indicates operation was rejected because the system is - // not in a state required for the operation's execution. For example, doing - // an rmdir operation on a directory that is non-empty, or on a non-directory - // object, or when having conflicting read-modify-write on the same resource. - FailedPrecondition ErrorCode = "failed_precondition" - - // Aborted indicates the operation was aborted, typically due to a concurrency - // issue like sequencer check failures, transaction aborts, etc. - Aborted ErrorCode = "aborted" - - // OutOfRange means operation was attempted past the valid range. For example, - // seeking or reading past end of a paginated collection. - // - // Unlike InvalidArgument, this error indicates a problem that may be fixed if - // the system state changes (i.e. adding more items to the collection). - // - // There is a fair bit of overlap between FailedPrecondition and OutOfRange. - // We recommend using OutOfRange (the more specific error) when it applies so - // that callers who are iterating through a space can easily look for an - // OutOfRange error to detect when they are done. - OutOfRange ErrorCode = "out_of_range" - - // Unimplemented indicates operation is not implemented or not - // supported/enabled in this service. - Unimplemented ErrorCode = "unimplemented" - - // Internal errors. When some invariants expected by the underlying system - // have been broken. In other words, something bad happened in the library or - // backend service. Do not confuse with HTTP Internal Server Error; an - // Internal error could also happen on the client code, i.e. when parsing a - // server response. - Internal ErrorCode = "internal" - - // Unavailable indicates the service is currently unavailable. This is a most - // likely a transient condition and may be corrected by retrying with a - // backoff. - Unavailable ErrorCode = "unavailable" - - // DataLoss indicates unrecoverable data loss or corruption. - DataLoss ErrorCode = "data_loss" - - // NoError is the zero-value, is considered an empty error and should not be - // used. - NoError ErrorCode = "" -) - -// ServerHTTPStatusFromErrorCode maps a Twirp error type into a similar HTTP -// response status. It is used by the Twirp server handler to set the HTTP -// response status code. Returns 0 if the ErrorCode is invalid. -func ServerHTTPStatusFromErrorCode(code ErrorCode) int { - switch code { - case Canceled: - return 408 // RequestTimeout - case Unknown: - return 500 // Internal Server Error - case InvalidArgument: - return 400 // BadRequest - case Malformed: - return 400 // BadRequest - case DeadlineExceeded: - return 408 // RequestTimeout - case NotFound: - return 404 // Not Found - case BadRoute: - return 404 // Not Found - case AlreadyExists: - return 409 // Conflict - case PermissionDenied: - return 403 // Forbidden - case Unauthenticated: - return 401 // Unauthorized - case ResourceExhausted: - return 429 // Too Many Requests - case FailedPrecondition: - return 412 // Precondition Failed - case Aborted: - return 409 // Conflict - case OutOfRange: - return 400 // Bad Request - case Unimplemented: - return 501 // Not Implemented - case Internal: - return 500 // Internal Server Error - case Unavailable: - return 503 // Service Unavailable - case DataLoss: - return 500 // Internal Server Error - case NoError: - return 200 // OK - default: - return 0 // Invalid! - } -} - -// IsValidErrorCode returns true if is one of the valid predefined constants. -func IsValidErrorCode(code ErrorCode) bool { - return ServerHTTPStatusFromErrorCode(code) != 0 -} - -// twirp.Error implementation -type twerr struct { - code ErrorCode - msg string - meta map[string]string -} - -func (e *twerr) Code() ErrorCode { return e.code } -func (e *twerr) Msg() string { return e.msg } - -func (e *twerr) Meta(key string) string { - if e.meta != nil { - return e.meta[key] // also returns "" if key is not in meta map - } - return "" -} - -func (e *twerr) WithMeta(key string, value string) Error { - newErr := &twerr{ - code: e.code, - msg: e.msg, - meta: make(map[string]string, len(e.meta)), - } - for k, v := range e.meta { - newErr.meta[k] = v - } - newErr.meta[key] = value - return newErr -} - -func (e *twerr) MetaMap() map[string]string { - return e.meta -} - -func (e *twerr) Error() string { - return fmt.Sprintf("twirp error %s: %s", e.code, e.msg) -} - -// wrappedErr is the error returned by twirp.InternalErrorWith(err), which is used by clients. -// Implements Unwrap() to allow go 1.13+ errors.Is/As checks, -// and Cause() to allow (github.com/pkg/errors).Unwrap. -type wrappedErr struct { - wrapper Error - cause error -} - -func (e *wrappedErr) Code() ErrorCode { return e.wrapper.Code() } -func (e *wrappedErr) Msg() string { return e.wrapper.Msg() } -func (e *wrappedErr) Meta(key string) string { return e.wrapper.Meta(key) } -func (e *wrappedErr) MetaMap() map[string]string { return e.wrapper.MetaMap() } -func (e *wrappedErr) Error() string { return e.wrapper.Error() } -func (e *wrappedErr) WithMeta(key string, val string) Error { - return &wrappedErr{ - wrapper: e.wrapper.WithMeta(key, val), - cause: e.cause, - } -} -func (e *wrappedErr) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As -func (e *wrappedErr) Cause() error { return e.cause } // for github.com/pkg/errors - -// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). -// Useful outside of the Twirp server (e.g. http middleware). -// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) -func WriteError(resp http.ResponseWriter, err error) error { - var twerr Error - if !errors.As(err, &twerr) { - twerr = InternalErrorWith(err) - } - - statusCode := ServerHTTPStatusFromErrorCode(twerr.Code()) - respBody := marshalErrorToJSON(twerr) - - resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON - resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) - resp.WriteHeader(statusCode) // set HTTP status code and send response - - _, writeErr := resp.Write(respBody) - if writeErr != nil { - return writeErr - } - return nil -} - -// JSON serialization for errors -type twerrJSON struct { - Code string `json:"code"` - Msg string `json:"msg"` - Meta map[string]string `json:"meta,omitempty"` -} - -// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. -// If serialization fails, it will use a descriptive Internal error instead. -func marshalErrorToJSON(twerr Error) []byte { - // make sure that msg is not too large - msg := twerr.Msg() - if len(msg) > 1e6 { - msg = msg[:1e6] - } - - tj := twerrJSON{ - Code: string(twerr.Code()), - Msg: msg, - Meta: twerr.MetaMap(), - } - - buf, err := json.Marshal(&tj) - if err != nil { - buf = []byte("{\"type\": \"" + Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback - } - - return buf -} |
