Allow data store to set the status code for errors
Closes https://github.com/tus/tusd/issues/77
This commit is contained in:
parent
675f8fcf04
commit
f50f03fe6f
20
metrics.go
20
metrics.go
|
@ -31,11 +31,14 @@ func (m Metrics) incRequestsTotal(method string) {
|
||||||
// incErrorsTotal increases the counter for this error atomically by one.
|
// incErrorsTotal increases the counter for this error atomically by one.
|
||||||
func (m Metrics) incErrorsTotal(err error) {
|
func (m Metrics) incErrorsTotal(err error) {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
if _, ok := ErrStatusCodes[err]; !ok {
|
|
||||||
msg = "system error"
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.AddUint64(m.ErrorsTotal[msg], 1)
|
if addr, ok := m.ErrorsTotal[msg]; ok {
|
||||||
|
atomic.AddUint64(addr, 1)
|
||||||
|
} else {
|
||||||
|
addr := new(uint64)
|
||||||
|
*addr = 1
|
||||||
|
m.ErrorsTotal[msg] = addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// incBytesReceived increases the number of received bytes atomically be the
|
// incBytesReceived increases the number of received bytes atomically be the
|
||||||
|
@ -78,13 +81,6 @@ func newMetrics() Metrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newErrorsTotalMap() map[string]*uint64 {
|
func newErrorsTotalMap() map[string]*uint64 {
|
||||||
m := make(map[string]*uint64, len(ErrStatusCodes)+1)
|
m := make(map[string]*uint64, 20)
|
||||||
|
|
||||||
for err := range ErrStatusCodes {
|
|
||||||
m[err.Error()] = new(uint64)
|
|
||||||
}
|
|
||||||
|
|
||||||
m["system error"] = new(uint64)
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,39 +20,46 @@ var (
|
||||||
reForwardedProto = regexp.MustCompile(`proto=(https?)`)
|
reForwardedProto = regexp.MustCompile(`proto=(https?)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// HTTPError represents an error with an additional status code attached
|
||||||
ErrUnsupportedVersion = errors.New("unsupported version")
|
// which may be used when this error is sent in a HTTP response.
|
||||||
ErrMaxSizeExceeded = errors.New("maximum size exceeded")
|
// See the net/http package for standardized status codes.
|
||||||
ErrInvalidContentType = errors.New("missing or invalid Content-Type header")
|
type HTTPError interface {
|
||||||
ErrInvalidUploadLength = errors.New("missing or invalid Upload-Length header")
|
error
|
||||||
ErrInvalidOffset = errors.New("missing or invalid Upload-Offset header")
|
StatusCode() int
|
||||||
ErrNotFound = errors.New("upload not found")
|
|
||||||
ErrFileLocked = errors.New("file currently locked")
|
|
||||||
ErrMismatchOffset = errors.New("mismatched offset")
|
|
||||||
ErrSizeExceeded = errors.New("resource's size exceeded")
|
|
||||||
ErrNotImplemented = errors.New("feature not implemented")
|
|
||||||
ErrUploadNotFinished = errors.New("one of the partial uploads is not finished")
|
|
||||||
ErrInvalidConcat = errors.New("invalid Upload-Concat header")
|
|
||||||
ErrModifyFinal = errors.New("modifying a final upload is not allowed")
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTP status codes sent in the response when the specific error is returned.
|
|
||||||
var ErrStatusCodes = map[error]int{
|
|
||||||
ErrUnsupportedVersion: http.StatusPreconditionFailed,
|
|
||||||
ErrMaxSizeExceeded: http.StatusRequestEntityTooLarge,
|
|
||||||
ErrInvalidContentType: http.StatusBadRequest,
|
|
||||||
ErrInvalidUploadLength: http.StatusBadRequest,
|
|
||||||
ErrInvalidOffset: http.StatusBadRequest,
|
|
||||||
ErrNotFound: http.StatusNotFound,
|
|
||||||
ErrFileLocked: 423, // Locked (WebDAV) (RFC 4918)
|
|
||||||
ErrMismatchOffset: http.StatusConflict,
|
|
||||||
ErrSizeExceeded: http.StatusRequestEntityTooLarge,
|
|
||||||
ErrNotImplemented: http.StatusNotImplemented,
|
|
||||||
ErrUploadNotFinished: http.StatusBadRequest,
|
|
||||||
ErrInvalidConcat: http.StatusBadRequest,
|
|
||||||
ErrModifyFinal: http.StatusForbidden,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type httpError struct {
|
||||||
|
error
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err httpError) StatusCode() int {
|
||||||
|
return err.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPError adds the given status code to the provided error and returns
|
||||||
|
// the new error instance. The status code may be used in corresponding HTTP
|
||||||
|
// responses. See the net/http package for standardized status codes.
|
||||||
|
func NewHTTPError(err error, statusCode int) HTTPError {
|
||||||
|
return httpError{err, statusCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnsupportedVersion = NewHTTPError(errors.New("unsupported version"), http.StatusPreconditionFailed)
|
||||||
|
ErrMaxSizeExceeded = NewHTTPError(errors.New("maximum size exceeded"), http.StatusRequestEntityTooLarge)
|
||||||
|
ErrInvalidContentType = NewHTTPError(errors.New("missing or invalid Content-Type header"), http.StatusBadRequest)
|
||||||
|
ErrInvalidUploadLength = NewHTTPError(errors.New("missing or invalid Upload-Length header"), http.StatusBadRequest)
|
||||||
|
ErrInvalidOffset = NewHTTPError(errors.New("missing or invalid Upload-Offset header"), http.StatusBadRequest)
|
||||||
|
ErrNotFound = NewHTTPError(errors.New("upload not found"), http.StatusNotFound)
|
||||||
|
ErrFileLocked = NewHTTPError(errors.New("file currently locked"), 423) // Locked (WebDAV) (RFC 4918)
|
||||||
|
ErrMismatchOffset = NewHTTPError(errors.New("mismatched offset"), http.StatusConflict)
|
||||||
|
ErrSizeExceeded = NewHTTPError(errors.New("resource's size exceeded"), http.StatusRequestEntityTooLarge)
|
||||||
|
ErrNotImplemented = NewHTTPError(errors.New("feature not implemented"), http.StatusNotImplemented)
|
||||||
|
ErrUploadNotFinished = NewHTTPError(errors.New("one of the partial uploads is not finished"), http.StatusBadRequest)
|
||||||
|
ErrInvalidConcat = NewHTTPError(errors.New("invalid Upload-Concat header"), http.StatusBadRequest)
|
||||||
|
ErrModifyFinal = NewHTTPError(errors.New("modifying a final upload is not allowed"), http.StatusForbidden)
|
||||||
|
)
|
||||||
|
|
||||||
// UnroutedHandler exposes methods to handle requests as part of the tus protocol,
|
// UnroutedHandler exposes methods to handle requests as part of the tus protocol,
|
||||||
// such as PostFile, HeadFile, PatchFile and DelFile. In addition the GetFile method
|
// such as PostFile, HeadFile, PatchFile and DelFile. In addition the GetFile method
|
||||||
// is provided which is, however, not part of the specification.
|
// is provided which is, however, not part of the specification.
|
||||||
|
@ -593,9 +600,9 @@ func (handler *UnroutedHandler) sendError(w http.ResponseWriter, r *http.Request
|
||||||
err = ErrNotFound
|
err = ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
status, ok := ErrStatusCodes[err]
|
status := 500
|
||||||
if !ok {
|
if statusErr, ok := err.(HTTPError); ok {
|
||||||
status = 500
|
status = statusErr.StatusCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
reason := err.Error() + "\n"
|
reason := err.Error() + "\n"
|
||||||
|
|
Loading…
Reference in New Issue