github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsblock/server_errors.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfsblock
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"strconv"
    11  
    12  	"github.com/keybase/client/go/libkb"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    15  )
    16  
    17  const (
    18  	// StatusCodeServerError is the error code for a generic block server error.
    19  	StatusCodeServerError = 2700
    20  	// StatusCodeServerErrorBadRequest is the error code for a generic client error.
    21  	StatusCodeServerErrorBadRequest = 2701
    22  	// StatusCodeServerErrorUnauthorized is the error code for when the session has not been validated
    23  	StatusCodeServerErrorUnauthorized = 2702
    24  	// StatusCodeServerErrorOverQuota is the error code for when the user has exceeded his quota
    25  	StatusCodeServerErrorOverQuota = 2703
    26  	// StatusCodeServerErrorBlockNonExistent is the error code for when bserver cannot find a block
    27  	StatusCodeServerErrorBlockNonExistent = 2704
    28  	// StatusCodeServerErrorBlockArchived is the error code for a block has been archived
    29  	StatusCodeServerErrorBlockArchived = 2705
    30  	// StatusCodeServerErrorNoPermission is the error code for when there's no permission
    31  	StatusCodeServerErrorNoPermission = 2706
    32  	// StatusCodeServerErrorBlockDeleted is the error code for a block has been deleted
    33  	StatusCodeServerErrorBlockDeleted = 2707
    34  	// StatusCodeServerErrorNonceNonExistent is the error code when a nonce cannot be found
    35  	StatusCodeServerErrorNonceNonExistent = 2708
    36  	// StatusCodeServerErrorMaxRefExceeded is the error code to indicate there are too many refs to a block
    37  	StatusCodeServerErrorMaxRefExceeded = 2709
    38  	// StatusCodeServerErrorThrottle is the error code to indicate the client should initiate backoff.
    39  	StatusCodeServerErrorThrottle = 2799
    40  )
    41  
    42  // ServerError is a generic bserver-side error.
    43  type ServerError struct {
    44  	Msg string
    45  }
    46  
    47  // ToStatus implements the ExportableError interface for ServerError.
    48  func (e ServerError) ToStatus() (s keybase1.Status) {
    49  	s.Code = StatusCodeServerError
    50  	s.Name = "SERVER_ERROR"
    51  	s.Desc = e.Msg
    52  	return
    53  }
    54  
    55  // Error implements the Error interface for ServerError.
    56  func (e ServerError) Error() string {
    57  	return "ServerError{" + e.Msg + "}"
    58  }
    59  
    60  // ServerErrorBadRequest is a generic client-side error.
    61  type ServerErrorBadRequest struct {
    62  	Msg string
    63  }
    64  
    65  // ToStatus implements the ExportableError interface for ServerError.
    66  func (e ServerErrorBadRequest) ToStatus() (s keybase1.Status) {
    67  	s.Code = StatusCodeServerErrorBadRequest
    68  	s.Name = "BAD_REQUEST"
    69  	s.Desc = e.Msg
    70  	return
    71  }
    72  
    73  // Error implements the Error interface for ServerError.
    74  func (e ServerErrorBadRequest) Error() string {
    75  	if e.Msg == "" {
    76  		return "Server: bad client request"
    77  	}
    78  	return "ServerErrorBadRequest{" + e.Msg + "}"
    79  }
    80  
    81  // ServerErrorUnauthorized is a generic client-side error.
    82  type ServerErrorUnauthorized struct {
    83  	Msg string
    84  }
    85  
    86  // ToStatus implements the ExportableError interface for ServerErrorUnauthorized.
    87  func (e ServerErrorUnauthorized) ToStatus() (s keybase1.Status) {
    88  	s.Code = StatusCodeServerErrorUnauthorized
    89  	s.Name = "SESSION_UNAUTHORIZED"
    90  	s.Desc = e.Msg
    91  	return
    92  }
    93  
    94  // Error implements the Error interface for ServerErrorUnauthorized.
    95  func (e ServerErrorUnauthorized) Error() string {
    96  	if e.Msg == "" {
    97  		return "Server: session not validated"
    98  	}
    99  	return "ServerErrorUnauthorized{" + e.Msg + "}"
   100  }
   101  
   102  // ServerErrorOverQuota is returned when a user is over quota.
   103  type ServerErrorOverQuota struct {
   104  	Msg string
   105  	// Usage indicates the current usage
   106  	Usage int64
   107  	// Limit indicates the current quota limit
   108  	Limit int64
   109  	// Throttled indicates if request has not been completed due to server throttle
   110  	Throttled bool
   111  }
   112  
   113  // ToStatus implements the ExportableError interface for ServerErrorOverQuota.
   114  func (e ServerErrorOverQuota) ToStatus() (s keybase1.Status) {
   115  	s.Code = StatusCodeServerErrorOverQuota
   116  	s.Name = "QUOTA_EXCEEDED"
   117  	s.Desc = e.Msg
   118  	s.Fields = append(s.Fields, keybase1.StringKVPair{
   119  		Key:   "QUOTA_USAGE",
   120  		Value: strconv.FormatInt(e.Usage, 10),
   121  	})
   122  	s.Fields = append(s.Fields, keybase1.StringKVPair{
   123  		Key:   "QUOTA_LIMIT",
   124  		Value: strconv.FormatInt(e.Limit, 10),
   125  	})
   126  	s.Fields = append(s.Fields, keybase1.StringKVPair{
   127  		Key:   "QUOTA_THROTTLE",
   128  		Value: strconv.FormatBool(e.Throttled),
   129  	})
   130  	return
   131  }
   132  
   133  // Error implements the Error interface for ServerErrorOverQuota.
   134  func (e ServerErrorOverQuota) Error() string {
   135  	return fmt.Sprintf(
   136  		"ServerErrorOverQuota{Msg: %q, Usage: %d, Limit: %d, Throttled: %t}",
   137  		e.Msg, e.Usage, e.Limit, e.Throttled)
   138  }
   139  
   140  // ServerErrorBlockNonExistent is an exportable error from bserver
   141  type ServerErrorBlockNonExistent struct {
   142  	Msg string
   143  }
   144  
   145  // ToStatus implements the ExportableError interface for ServerErrorBlockNonExistent
   146  func (e ServerErrorBlockNonExistent) ToStatus() (s keybase1.Status) {
   147  	s.Code = StatusCodeServerErrorBlockNonExistent
   148  	s.Name = "BLOCK_NONEXISTENT"
   149  	s.Desc = e.Msg
   150  	return
   151  }
   152  
   153  // Error implements the Error interface for ServerErrorBlockNonExistent.
   154  func (e ServerErrorBlockNonExistent) Error() string {
   155  	if e.Msg == "" {
   156  		return "Server: block does not exist"
   157  	}
   158  	return "ServerErrorBlockNonExistent{" + e.Msg + "}"
   159  }
   160  
   161  // ServerErrorBlockArchived is an exportable error from bserver
   162  type ServerErrorBlockArchived struct {
   163  	Msg string
   164  }
   165  
   166  // ToStatus implements the ExportableError interface for ServerErrorBlockArchived
   167  func (e ServerErrorBlockArchived) ToStatus() (s keybase1.Status) {
   168  	s.Code = StatusCodeServerErrorBlockArchived
   169  	s.Name = "BLOCK_ARCHIVED"
   170  	s.Desc = e.Msg
   171  	return
   172  }
   173  
   174  // Error implements the Error interface for ServerErrorBlockArchived.
   175  func (e ServerErrorBlockArchived) Error() string {
   176  	if e.Msg == "" {
   177  		return "Server: block is archived"
   178  	}
   179  	return "ServerErrorBlockArchived{" + e.Msg + "}"
   180  }
   181  
   182  // ServerErrorBlockDeleted is an exportable error from bserver
   183  type ServerErrorBlockDeleted struct {
   184  	Msg string
   185  }
   186  
   187  // ToStatus implements the ExportableError interface for ServerErrorBlockDeleted
   188  func (e ServerErrorBlockDeleted) ToStatus() (s keybase1.Status) {
   189  	s.Code = StatusCodeServerErrorBlockDeleted
   190  	s.Name = "BLOCK_DELETED"
   191  	s.Desc = e.Msg
   192  	return
   193  }
   194  
   195  // Error implements the Error interface for ServerErrorBlockDeleted
   196  func (e ServerErrorBlockDeleted) Error() string {
   197  	if e.Msg == "" {
   198  		return "Server: block is deleted"
   199  	}
   200  	return "ServerErrorBlockDeleted{" + e.Msg + "}"
   201  }
   202  
   203  // ServerErrorNoPermission is an exportable error from bserver
   204  type ServerErrorNoPermission struct {
   205  	Msg string
   206  }
   207  
   208  // ToStatus implements the ExportableError interface for ServerErrorBlockArchived
   209  func (e ServerErrorNoPermission) ToStatus() (s keybase1.Status) {
   210  	s.Code = StatusCodeServerErrorNoPermission
   211  	s.Name = "NO_PERMISSION"
   212  	s.Desc = e.Msg
   213  	return
   214  }
   215  
   216  // Error implements the Error interface for ServerErrorNoPermission.
   217  func (e ServerErrorNoPermission) Error() string {
   218  	if e.Msg == "" {
   219  		return "Server: permission denied"
   220  	}
   221  	return "ServerErrorNoPermission{" + e.Msg + "}"
   222  }
   223  
   224  // ServerErrorNonceNonExistent is an exportable error from bserver
   225  type ServerErrorNonceNonExistent struct {
   226  	Msg string
   227  }
   228  
   229  // ToStatus implements the ExportableError interface for ServerErrorNonceNonExistent
   230  func (e ServerErrorNonceNonExistent) ToStatus() (s keybase1.Status) {
   231  	s.Code = StatusCodeServerErrorNonceNonExistent
   232  	s.Name = "BLOCK_NONCENONEXISTENT"
   233  	s.Desc = e.Msg
   234  	return
   235  }
   236  
   237  // Error implements the Error interface for ServerErrornonceNonExistent.
   238  func (e ServerErrorNonceNonExistent) Error() string {
   239  	if e.Msg == "" {
   240  		return "Server: reference nonce does not exist"
   241  	}
   242  	return "ServerErrorNonceNonExistent{" + e.Msg + "}"
   243  }
   244  
   245  // ServerErrorMaxRefExceeded is an exportable error from bserver
   246  type ServerErrorMaxRefExceeded struct {
   247  	Msg string
   248  }
   249  
   250  // ToStatus implements the ExportableError interface for ServerErrorMaxRefExceeded
   251  func (e ServerErrorMaxRefExceeded) ToStatus() (s keybase1.Status) {
   252  	s.Code = StatusCodeServerErrorMaxRefExceeded
   253  	s.Name = "BLOCK_MAXREFEXCEEDED"
   254  	s.Desc = e.Msg
   255  	return
   256  }
   257  
   258  // Error implements the Error interface for ServerErrorMaxRefExceeded
   259  func (e ServerErrorMaxRefExceeded) Error() string {
   260  	if e.Msg == "" {
   261  		return "Server: maximum allowed number of references exceeded"
   262  	}
   263  	return "ServerErrorMaxRefExceeded{" + e.Msg + "}"
   264  }
   265  
   266  // ServerErrorThrottle is returned when the server wants the client to backoff.
   267  type ServerErrorThrottle struct {
   268  	Msg string
   269  }
   270  
   271  // Error implements the Error interface for ServerErrorThrottle.
   272  func (e ServerErrorThrottle) Error() string {
   273  	return "ServerErrorThrottle{" + e.Msg + "}"
   274  }
   275  
   276  // ToStatus implements the ExportableError interface for ServerErrorThrottle.
   277  func (e ServerErrorThrottle) ToStatus() (s keybase1.Status) {
   278  	s.Code = StatusCodeServerErrorThrottle
   279  	s.Name = "ERROR_THROTTLE"
   280  	s.Desc = e.Msg
   281  	return
   282  }
   283  
   284  // ServerErrorUnwrapper unwraps errors from a remote block server.
   285  type ServerErrorUnwrapper struct{}
   286  
   287  var _ rpc.ErrorUnwrapper = ServerErrorUnwrapper{}
   288  
   289  // MakeArg implements rpc.ErrorUnwrapper.
   290  func (eu ServerErrorUnwrapper) MakeArg() interface{} {
   291  	return &keybase1.Status{}
   292  }
   293  
   294  // UnwrapError implements rpc.ErrorUnwrapper.
   295  func (eu ServerErrorUnwrapper) UnwrapError(arg interface{}) (appError error, dispatchError error) {
   296  	s, ok := arg.(*keybase1.Status)
   297  	if !ok {
   298  		return nil, errors.New("Error converting arg to keybase1.Status object in ServerErrorUnwrapper.UnwrapError")
   299  	}
   300  
   301  	if s == nil || s.Code == 0 {
   302  		return nil, nil
   303  	}
   304  
   305  	switch s.Code {
   306  	case StatusCodeServerError:
   307  		appError = ServerError{Msg: s.Desc}
   308  	case StatusCodeServerErrorBadRequest:
   309  		appError = ServerErrorBadRequest{Msg: s.Desc}
   310  	case StatusCodeServerErrorUnauthorized:
   311  		appError = ServerErrorUnauthorized{Msg: s.Desc}
   312  	case StatusCodeServerErrorOverQuota:
   313  		quotaErr := ServerErrorOverQuota{Msg: s.Desc}
   314  		for _, f := range s.Fields {
   315  			switch {
   316  			case f.Key == "QUOTA_USAGE":
   317  				quotaErr.Usage, _ = strconv.ParseInt(f.Value, 10, 64)
   318  			case f.Key == "QUOTA_LIMIT":
   319  				quotaErr.Limit, _ = strconv.ParseInt(f.Value, 10, 64)
   320  			case f.Key == "QUOTA_THROTTLE":
   321  				quotaErr.Throttled, _ = strconv.ParseBool(f.Value)
   322  			}
   323  		}
   324  		appError = quotaErr
   325  	case StatusCodeServerErrorBlockNonExistent:
   326  		appError = ServerErrorBlockNonExistent{Msg: s.Desc}
   327  	case StatusCodeServerErrorBlockArchived:
   328  		appError = ServerErrorBlockArchived{Msg: s.Desc}
   329  	case StatusCodeServerErrorNoPermission:
   330  		appError = ServerErrorNoPermission{Msg: s.Desc}
   331  	case StatusCodeServerErrorThrottle:
   332  		appError = ServerErrorThrottle{Msg: s.Desc}
   333  	case StatusCodeServerErrorBlockDeleted:
   334  		appError = ServerErrorBlockDeleted{Msg: s.Desc}
   335  	case StatusCodeServerErrorNonceNonExistent:
   336  		appError = ServerErrorNonceNonExistent{Msg: s.Desc}
   337  	case StatusCodeServerErrorMaxRefExceeded:
   338  		appError = ServerErrorMaxRefExceeded{Msg: s.Desc}
   339  	default:
   340  		ase := libkb.AppStatusError{
   341  			Code:   s.Code,
   342  			Name:   s.Name,
   343  			Desc:   s.Desc,
   344  			Fields: make(map[string]string),
   345  		}
   346  		for _, f := range s.Fields {
   347  			ase.Fields[f.Key] = f.Value
   348  		}
   349  		appError = ase
   350  	}
   351  
   352  	return appError, nil
   353  }
   354  
   355  // IsThrottleError returns whether or not the given error signals
   356  // throttling.
   357  func IsThrottleError(err error) bool {
   358  	if _, ok := err.(ServerErrorThrottle); ok {
   359  		return true
   360  	}
   361  	if quotaErr, ok := err.(ServerErrorOverQuota); ok && quotaErr.Throttled {
   362  		return true
   363  	}
   364  	return false
   365  }