-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherror.go
152 lines (134 loc) · 4.51 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2019 - MinIO, Inc. All rights reserved.
// Use of this source code is governed by the AGPLv3
// license that can be found in the LICENSE file.
package kes
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
)
var (
// ErrNotAllowed represents a KES server response returned when the
// client has not sufficient policy permissions to perform a particular
// operation.
ErrNotAllowed = NewError(http.StatusForbidden, "prohibited by policy")
// ErrKeyNotFound represents a KES server response returned when a client
// tries to access or use a cryptographic key which does not exist.
ErrKeyNotFound = NewError(http.StatusNotFound, "key does not exist")
// ErrKeyExists represents a KES server response returned when a client tries
// to create a cryptographic key which already exists.
ErrKeyExists = NewError(http.StatusBadRequest, "key does already exist")
// ErrPolicyNotFound represents a KES server response returned when a client
// tries to access a policy which does not exist.
ErrPolicyNotFound = NewError(http.StatusNotFound, "policy does not exist")
// ErrDecrypt represents a KES server response returned when the server fails
// to decrypt an encrypted ciphertext. It may occur when a client uses the
// the wrong key or the ciphertext has been (maliciously) modified.
ErrDecrypt = NewError(http.StatusBadRequest, "decryption failed: ciphertext is not authentic")
)
// Error is the type of client-server API errors.
// A Client returns an Error if a server responds
// with a well-formed error message.
//
// An Error contains the HTTP status code sent by
// the server. Errors with the same status code and
// error message are equal. In particular:
// ErrKeyExists == NewError(400, "key does already exist") // true
//
// The client may distinguish errors as following:
// switch err := client.CreateKey("example-key"); err {
// case nil: // Success!
// case ErrKeyExists:
// // The key "example-key" already exists.
// case ErrNotAllowed:
// // We don't have the permission to create this key.
// default:
// // Something else went wrong.
// }
type Error struct {
code int
message string
}
// NewError returns a new Error with the given
// HTTP status code and error message.
//
// Two errors with the same status code and
// error message are equal.
func NewError(code int, msg string) Error {
return Error{
code: code,
message: msg,
}
}
// Status returns the HTTP status code of the error.
func (e Error) Status() int { return e.code }
func (e Error) Error() string { return e.message }
// parseErrorResponse returns an error containing
// the response status code and response body
// as error message if the response is an error
// response - i.e. status code >= 400.
//
// If the response status code is < 400, e.g. 200 OK,
// parseErrorResponse returns nil and does not attempt
// to read or close the response body.
//
// If resp is an error response, parseErrorResponse reads
// and closes the response body.
func parseErrorResponse(resp *http.Response) error {
if resp == nil || resp.StatusCode < 400 {
return nil
}
if resp.Body == nil {
return NewError(resp.StatusCode, "")
}
defer resp.Body.Close()
const MaxBodySize = 1 << 20
var size = resp.ContentLength
if size < 0 || size > MaxBodySize {
size = MaxBodySize
}
contentType := strings.TrimSpace(resp.Header.Get("Content-Type"))
if strings.HasPrefix(contentType, "application/json") {
type Response struct {
Message string `json:"message"`
}
var response Response
if err := json.NewDecoder(io.LimitReader(resp.Body, size)).Decode(&response); err != nil {
return err
}
return NewError(resp.StatusCode, response.Message)
}
var sb strings.Builder
if _, err := io.Copy(&sb, io.LimitReader(resp.Body, size)); err != nil {
return err
}
return NewError(resp.StatusCode, sb.String())
}
func parseErrorTrailer(trailer http.Header) error {
if _, ok := trailer["Status"]; !ok {
return errors.New("kes: unexpected EOF: no HTTP status trailer")
}
status, err := strconv.Atoi(trailer.Get("Status"))
if err != nil {
return fmt.Errorf("kes: invalid HTTP trailer - Status: %q", trailer.Get("Status"))
}
if status == http.StatusOK {
return nil
}
errMessage := trailer.Get("Error")
if errMessage == "" {
return NewError(status, "")
}
type Response struct {
Message string `json:"message"`
}
var response Response
if err = json.Unmarshal([]byte(errMessage), &response); err != nil {
return err
}
return NewError(status, response.Message)
}