-
Notifications
You must be signed in to change notification settings - Fork 7
/
errors.go
183 lines (155 loc) · 4.07 KB
/
errors.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright 2014 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package main
import (
"encoding/json"
"errors"
"io"
"net/http"
"os"
"strings"
"bitbucket.org/tshannon/freehold/data"
"bitbucket.org/tshannon/freehold/fail"
"bitbucket.org/tshannon/freehold/log"
"bitbucket.org/tshannon/freehold/permission"
"bitbucket.org/tshannon/freehold/resource"
"bitbucket.org/tshannon/freehold/setting"
)
const (
acceptHTML = "text/html"
)
// Err404 is a standard 404 error response
var Err404 = errors.New("Resource not found")
func errHandled(err error, w http.ResponseWriter, auth *Auth) bool {
if err == nil {
return false
}
if auth == nil {
auth = &Auth{
AuthType: authTypeNone,
}
}
var status, errMsg string
switch err.(type) {
case *fail.Fail:
status = statusFail
if fail.IsEqual(err, Err404) {
respond404JSON(w, err.(*fail.Fail).Data.(string))
return true
}
case *http.ProtocolError, *json.SyntaxError, *json.UnmarshalTypeError:
//Hardcoded external errors which can bubble up to the end users
// without exposing internal server information, make them failures
err = fail.NewFromErr(err, nil)
status = statusFail
log.Fail(err.(*fail.Fail), auth.Username)
default:
status = statusError
log.Error(err)
if setting.Bool("FullClientErrors") {
errMsg = err.Error()
} else {
errMsg = "An internal server error has occurred"
}
}
if status == statusFail {
respondJsend(w, &JSend{
Status: status,
Message: err.(*fail.Fail).Message,
Data: err.(*fail.Fail).Data,
})
} else {
respondJsend(w, &JSend{
Status: status,
Message: errMsg,
})
}
return true
}
// four04 is a standard 404 response if request header accepts text/html
// they'll get a 404 page, otherwise a json response
func four04(w http.ResponseWriter, r *http.Request) {
accept := r.Header.Get("Accept")
if strings.Contains(accept, acceptHTML) {
respond404Page(w, r)
return
}
respond404JSON(w, r.URL.String())
}
func respond404JSON(w http.ResponseWriter, url string) {
if setting.Bool("Log404") {
log.NewEntry(log.Four04Type, "Resource not found: "+url)
}
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Content-Type", "application/json")
response := &JSend{
Status: statusFail,
Message: "Resource not found",
Data: url,
}
w.WriteHeader(http.StatusNotFound)
result, err := json.Marshal(response)
if err != nil {
log.Error(err)
return
}
w.Write(result)
}
func four04Fail(url string) error {
return fail.NewFromErr(Err404, url)
}
// four04Page returns a 404 status with custom page that can be set to any
// file in the system. This is displayed when a user tries to access a file
// that doesn't exist, or they don't have the right to know it exists
func respond404Page(w http.ResponseWriter, r *http.Request) {
if setting.Bool("Log404") {
log.NewEntry(log.Four04Type, "Resource not found: "+r.URL.String())
}
res := resource.NewFile(setting.String("404File"))
prm, err := permission.Get(res)
if err != nil {
log.Error(err)
http.NotFound(w, r)
return
}
if !prm.CanRead(nil) {
log.Error(errors.New("The 404File setting is pointing at a non-public file: " +
setting.String("404File")))
http.NotFound(w, r)
return
}
file, err := os.Open(res.Filepath())
defer file.Close()
if err != nil {
log.Error(err)
http.NotFound(w, r)
return
}
info, err := file.Stat()
if err != nil {
log.Error(err)
http.NotFound(w, r)
return
}
w.WriteHeader(http.StatusNotFound)
//Have to do this manually so 404 Status Code is preserved
io.CopyN(w, file, info.Size())
}
//ds404 is the response returned when a datastore entry is not found
func ds404(w http.ResponseWriter, r *http.Request, key *data.Key) {
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Content-Type", "application/json")
response := &JSend{
Status: statusFail,
Message: "Value not found for key",
Data: key,
}
w.WriteHeader(http.StatusNotFound)
result, err := json.Marshal(response)
if err != nil {
log.Error(err)
return
}
w.Write(result)
}