Files
AdGuardDNS/internal/backendgrpc/error.go
Ainar Garipov b4faca20be Sync v2.21.0
2026-04-17 13:49:23 +03:00

85 lines
2.3 KiB
Go

package backendgrpc
import (
"context"
"fmt"
"github.com/AdguardTeam/AdGuardDNS/internal/backendgrpc/dnspb"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// fixGRPCError converts a gRPC error into an application error, if necessary.
// That includes gRPC deadlines, which do not match [context.DeadlineExceeded]
// correctly.
//
// It also updates the backend gRPC metrics depending on the type, see
// [GRPCMetrics.IncrementErrorCount].
//
// TODO(e.burkov): Move error types to this package.
//
// TODO(a.garipov): Separate metrics reporting from error fixing.
func fixGRPCError(ctx context.Context, mtrc GRPCMetrics, err error) (res error) {
metricsType := GRPCErrOther
defer func() { mtrc.IncrementErrorCount(ctx, metricsType) }()
s, ok := status.FromError(err)
if !ok {
// Return the error as-is.
return err
}
// See https://github.com/grpc/grpc-go/issues/4822.
//
// TODO(d.kolyshev): Remove after the grpc-go issue is fixed.
if s.Code() == codes.DeadlineExceeded {
metricsType = GRPCErrTimeout
return fmt.Errorf("grpc: %w; original message: %s", context.DeadlineExceeded, err)
}
for _, d := range s.Details() {
switch structErr := d.(type) {
case *dnspb.AuthenticationFailedError:
metricsType = GRPCErrAuthentication
return &profiledb.AuthenticationFailedError{
Message: structErr.Message,
}
case *dnspb.BadRequestError:
metricsType = GRPCErrBadRequest
return &profiledb.BadRequestError{
Message: structErr.Message,
}
case *dnspb.DeviceQuotaExceededError:
metricsType = GRPCErrDeviceQuota
return &profiledb.DeviceQuotaExceededError{
Message: structErr.Message,
}
case *dnspb.NotFoundError:
metricsType = GRPCErrNotFound
// This error can currently only be returned from the certificate
// API, so return the error that it expects.
//
// TODO(a.garipov): Fix this and don't assume that this error is
// only returned there.
return fmt.Errorf("%s: %w", structErr.Message, tlsconfig.ErrCertificateNotFound)
case *dnspb.RateLimitedError:
metricsType = GRPCErrRateLimit
return &profiledb.RateLimitedError{
Message: structErr.Message,
RetryDelay: structErr.RetryDelay.AsDuration(),
}
}
}
// Return the error as-is.
return err
}