podman
192 строки · 4.9 Кб
1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package errors
16
17import (
18"encoding/json"
19"fmt"
20"net/http"
21"reflect"
22"strings"
23)
24
25// DefaultHTTPCode is used when the error Code cannot be used as an HTTP code.
26var DefaultHTTPCode = http.StatusUnprocessableEntity
27
28// Error represents a error interface all swagger framework errors implement
29type Error interface {
30error
31Code() int32
32}
33
34type apiError struct {
35code int32
36message string
37}
38
39func (a *apiError) Error() string {
40return a.message
41}
42
43func (a *apiError) Code() int32 {
44return a.code
45}
46
47// MarshalJSON implements the JSON encoding interface
48func (a apiError) MarshalJSON() ([]byte, error) {
49return json.Marshal(map[string]interface{}{
50"code": a.code,
51"message": a.message,
52})
53}
54
55// New creates a new API error with a code and a message
56func New(code int32, message string, args ...interface{}) Error {
57if len(args) > 0 {
58return &apiError{
59code: code,
60message: fmt.Sprintf(message, args...),
61}
62}
63return &apiError{
64code: code,
65message: message,
66}
67}
68
69// NotFound creates a new not found error
70func NotFound(message string, args ...interface{}) Error {
71if message == "" {
72message = "Not found"
73}
74return New(http.StatusNotFound, fmt.Sprintf(message, args...))
75}
76
77// NotImplemented creates a new not implemented error
78func NotImplemented(message string) Error {
79return New(http.StatusNotImplemented, message)
80}
81
82// MethodNotAllowedError represents an error for when the path matches but the method doesn't
83type MethodNotAllowedError struct {
84code int32
85Allowed []string
86message string
87}
88
89func (m *MethodNotAllowedError) Error() string {
90return m.message
91}
92
93// Code the error code
94func (m *MethodNotAllowedError) Code() int32 {
95return m.code
96}
97
98// MarshalJSON implements the JSON encoding interface
99func (m MethodNotAllowedError) MarshalJSON() ([]byte, error) {
100return json.Marshal(map[string]interface{}{
101"code": m.code,
102"message": m.message,
103"allowed": m.Allowed,
104})
105}
106
107func errorAsJSON(err Error) []byte {
108//nolint:errchkjson
109b, _ := json.Marshal(struct {
110Code int32 `json:"code"`
111Message string `json:"message"`
112}{err.Code(), err.Error()})
113return b
114}
115
116func flattenComposite(errs *CompositeError) *CompositeError {
117var res []error
118for _, er := range errs.Errors {
119switch e := er.(type) {
120case *CompositeError:
121if e != nil && len(e.Errors) > 0 {
122flat := flattenComposite(e)
123if len(flat.Errors) > 0 {
124res = append(res, flat.Errors...)
125}
126}
127default:
128if e != nil {
129res = append(res, e)
130}
131}
132}
133return CompositeValidationError(res...)
134}
135
136// MethodNotAllowed creates a new method not allowed error
137func MethodNotAllowed(requested string, allow []string) Error {
138msg := fmt.Sprintf("method %s is not allowed, but [%s] are", requested, strings.Join(allow, ","))
139return &MethodNotAllowedError{
140code: http.StatusMethodNotAllowed,
141Allowed: allow,
142message: msg,
143}
144}
145
146// ServeError implements the http error handler interface
147func ServeError(rw http.ResponseWriter, r *http.Request, err error) {
148rw.Header().Set("Content-Type", "application/json")
149switch e := err.(type) {
150case *CompositeError:
151er := flattenComposite(e)
152// strips composite errors to first element only
153if len(er.Errors) > 0 {
154ServeError(rw, r, er.Errors[0])
155} else {
156// guard against empty CompositeError (invalid construct)
157ServeError(rw, r, nil)
158}
159case *MethodNotAllowedError:
160rw.Header().Add("Allow", strings.Join(e.Allowed, ","))
161rw.WriteHeader(asHTTPCode(int(e.Code())))
162if r == nil || r.Method != http.MethodHead {
163_, _ = rw.Write(errorAsJSON(e))
164}
165case Error:
166value := reflect.ValueOf(e)
167if value.Kind() == reflect.Ptr && value.IsNil() {
168rw.WriteHeader(http.StatusInternalServerError)
169_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error")))
170return
171}
172rw.WriteHeader(asHTTPCode(int(e.Code())))
173if r == nil || r.Method != http.MethodHead {
174_, _ = rw.Write(errorAsJSON(e))
175}
176case nil:
177rw.WriteHeader(http.StatusInternalServerError)
178_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error")))
179default:
180rw.WriteHeader(http.StatusInternalServerError)
181if r == nil || r.Method != http.MethodHead {
182_, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, err.Error())))
183}
184}
185}
186
187func asHTTPCode(input int) int {
188if input >= 600 {
189return DefaultHTTPCode
190}
191return input
192}
193