github.com/jalateras/up@v0.1.5/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  // Errors handles error page support.
    86  type Errors struct {
    87  	next  http.Handler
    88  	pages errorpage.Pages
    89  }
    90  
    91  // New error pages handler.
    92  func New(c *up.Config, next http.Handler) (http.Handler, error) {
    93  	pages, err := errorpage.Load(c.ErrorPages.Dir)
    94  	if err != nil {
    95  		return nil, errors.Wrap(err, "loading error pages")
    96  	}
    97  
    98  	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    99  		mime, _ := accept.Negotiate(r.Header.Get("Accept"), "text/html")
   100  
   101  		if mime == "" {
   102  			next.ServeHTTP(w, r)
   103  			return
   104  		}
   105  
   106  		res := &response{ResponseWriter: w, pages: pages, config: c}
   107  		next.ServeHTTP(res, r)
   108  	})
   109  
   110  	return h, nil
   111  }