github.com/orangenpresse/up@v0.6.0/http/errorpages/errorpages.go (about) 1 // Package errorpages provides default and customizable 2 // error pages, via error.html, 5xx.html, or 500.html 3 // for example. 4 package errorpages 5 6 import ( 7 "io" 8 "net/http" 9 10 "github.com/pkg/errors" 11 accept "github.com/timewasted/go-accept-headers" 12 13 "github.com/apex/up" 14 "github.com/apex/up/internal/errorpage" 15 "github.com/apex/up/internal/logs" 16 "github.com/apex/up/internal/util" 17 ) 18 19 // log context. 20 var ctx = logs.Plugin("errorpages") 21 22 // response wrapper. 23 type response struct { 24 http.ResponseWriter 25 config *up.Config 26 pages errorpage.Pages 27 header bool 28 ignore bool 29 } 30 31 // WriteHeader implementation. 32 func (r *response) WriteHeader(code int) { 33 w := r.ResponseWriter 34 35 r.header = true 36 page := r.pages.Match(code) 37 38 if page == nil { 39 ctx.Debugf("did not match %d", code) 40 w.WriteHeader(code) 41 return 42 } 43 44 ctx.Debugf("matched %d with %q", code, page.Name) 45 46 data := struct { 47 StatusText string 48 StatusCode int 49 Variables map[string]interface{} 50 }{ 51 StatusText: http.StatusText(code), 52 StatusCode: code, 53 Variables: r.config.ErrorPages.Variables, 54 } 55 56 html, err := page.Render(data) 57 if err != nil { 58 ctx.WithError(err).Error("rendering error page") 59 http.Error(w, "Error rendering error page.", http.StatusInternalServerError) 60 return 61 } 62 63 r.ignore = true 64 util.ClearHeader(w.Header()) 65 w.Header().Set("Vary", "Accept") 66 w.Header().Set("Content-Type", "text/html; charset=utf-8") 67 w.WriteHeader(code) 68 io.WriteString(w, html) 69 } 70 71 // Write implementation. 72 func (r *response) Write(b []byte) (int, error) { 73 if r.ignore { 74 return len(b), nil 75 } 76 77 if !r.header { 78 r.WriteHeader(200) 79 return r.Write(b) 80 } 81 82 return r.ResponseWriter.Write(b) 83 } 84 85 // New error pages handler. 86 func New(c *up.Config, next http.Handler) (http.Handler, error) { 87 pages, err := errorpage.Load(c.ErrorPages.Dir) 88 if err != nil { 89 return nil, errors.Wrap(err, "loading error pages") 90 } 91 92 // we always have one "default" page, but it can be disabled 93 if len(pages) == 1 && c.ErrorPages.Disable { 94 return next, nil 95 } 96 97 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 98 mime, _ := accept.Negotiate(r.Header.Get("Accept"), "text/html") 99 100 if mime == "" { 101 next.ServeHTTP(w, r) 102 return 103 } 104 105 res := &response{ResponseWriter: w, pages: pages, config: c} 106 next.ServeHTTP(res, r) 107 }) 108 109 return h, nil 110 }