summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/errors.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-04-11 20:42:55 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-04-11 20:42:55 -0600
commit545f28008e0175491af030f8689cab2112fda9ed (patch)
treedc0d769486b8e05da7c67219292e9c0f95bb7f0b /modules/caddyhttp/errors.go
parentd42529348fac36f256d4e180ba1c2049f65646c1 (diff)
Begin implementing error handling and re-handling
Diffstat (limited to 'modules/caddyhttp/errors.go')
-rw-r--r--modules/caddyhttp/errors.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/modules/caddyhttp/errors.go b/modules/caddyhttp/errors.go
new file mode 100644
index 0000000..66cb2ca
--- /dev/null
+++ b/modules/caddyhttp/errors.go
@@ -0,0 +1,105 @@
+package caddyhttp
+
+import (
+ "fmt"
+ mathrand "math/rand"
+ "path"
+ "runtime"
+ "strings"
+
+ "bitbucket.org/lightcodelabs/caddy2"
+)
+
+// Error is a convenient way for a Handler to populate the
+// essential fields of a HandlerError. If err is itself a
+// HandlerError, then any essential fields that are not
+// set will be populated.
+func Error(statusCode int, err error) HandlerError {
+ const idLen = 9
+ if he, ok := err.(HandlerError); ok {
+ if he.ID == "" {
+ he.ID = randString(idLen, true)
+ }
+ if he.Trace == "" {
+ he.Trace = trace()
+ }
+ if he.StatusCode == 0 {
+ he.StatusCode = statusCode
+ }
+ return he
+ }
+ return HandlerError{
+ ID: randString(idLen, true),
+ StatusCode: statusCode,
+ Err: err,
+ Trace: trace(),
+ }
+}
+
+// HandlerError is a serializable representation of
+// an error from within an HTTP handler.
+type HandlerError struct {
+ Err error // the original error value and message
+ StatusCode int // the HTTP status code to associate with this error
+ Message string // an optional message that can be shown to the user
+ Recommendations []string // an optional list of things to try to resolve the error
+
+ ID string // generated; for identifying this error in logs
+ Trace string // produced from call stack
+}
+
+func (e HandlerError) Error() string {
+ var s string
+ if e.ID != "" {
+ s += fmt.Sprintf("{id=%s}", e.ID)
+ }
+ if e.Trace != "" {
+ s += " " + e.Trace
+ }
+ if e.StatusCode != 0 {
+ s += fmt.Sprintf(": HTTP %d", e.StatusCode)
+ }
+ if e.Err != nil {
+ s += ": " + e.Err.Error()
+ }
+ return strings.TrimSpace(s)
+}
+
+// randString returns a string of n random characters.
+// It is not even remotely secure OR a proper distribution.
+// But it's good enough for some things. It excludes certain
+// confusing characters like I, l, 1, 0, O, etc. If sameCase
+// is true, then uppercase letters are excluded.
+func randString(n int, sameCase bool) string {
+ if n <= 0 {
+ return ""
+ }
+ dict := []byte("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY23456789")
+ if sameCase {
+ dict = []byte("abcdefghijkmnpqrstuvwxyz0123456789")
+ }
+ b := make([]byte, n)
+ for i := range b {
+ b[i] = dict[mathrand.Int63()%int64(len(dict))]
+ }
+ return string(b)
+}
+
+func trace() string {
+ if pc, file, line, ok := runtime.Caller(2); ok {
+ filename := path.Base(file)
+ pkgAndFuncName := path.Base(runtime.FuncForPC(pc).Name())
+ return fmt.Sprintf("%s (%s:%d)", pkgAndFuncName, filename, line)
+ }
+ return ""
+}
+
+// ErrRehandle is a special error value that Handlers should return
+// from their ServeHTTP() method if the request is to be re-processed.
+// This error value is a sentinel value that should not be wrapped or
+// modified.
+var ErrRehandle = fmt.Errorf("rehandling request")
+
+// ErrorCtxKey is the context key to use when storing
+// an error (for use with context.Context).
+const ErrorCtxKey = caddy2.CtxKey("handler_chain_error")