@ -77,6 +77,20 @@ type errorJSON struct {
Error string
}
// AccessDeniedError is an error due to permissions.
type AccessDeniedError struct {
err error
}
func ( e * AccessDeniedError ) Error ( ) string { return fmt . Sprintf ( "Access denied: %v" , e . err ) }
func ( e * AccessDeniedError ) Unwrap ( ) error { return e . err }
// IsAccessDeniedError reports whether err is or wraps an AccessDeniedError.
func IsAccessDeniedError ( err error ) bool {
var ae * AccessDeniedError
return errors . As ( err , & ae )
}
// bestError returns either err, or if body contains a valid JSON
// object of type errorJSON, its non-empty error body.
func bestError ( err error , body [ ] byte ) error {
@ -87,6 +101,14 @@ func bestError(err error, body []byte) error {
return err
}
func errorMessageFromBody ( body [ ] byte ) string {
var j errorJSON
if err := json . Unmarshal ( body , & j ) ; err == nil && j . Error != "" {
return j . Error
}
return strings . TrimSpace ( string ( body ) )
}
var onVersionMismatch func ( clientVer , serverVer string )
// SetVersionMismatchHandler sets f as the version mismatch handler
@ -123,6 +145,9 @@ func send(ctx context.Context, method, path string, wantStatus int, body io.Read
return nil , err
}
if res . StatusCode != wantStatus {
if res . StatusCode == 403 {
return nil , & AccessDeniedError { errors . New ( errorMessageFromBody ( slurp ) ) }
}
err := fmt . Errorf ( "HTTP %s: %s (expected %v)" , res . Status , slurp , wantStatus )
return nil , bestError ( err , slurp )
}