github.com/hhsnopek/up@v0.1.1/http/redirects/redirects.go (about) 1 // Package redirects provides redirection and URL rewriting. 2 package redirects 3 4 import ( 5 "fmt" 6 "net/http" 7 8 "github.com/apex/log" 9 "github.com/apex/up" 10 "github.com/apex/up/internal/logs" 11 "github.com/apex/up/internal/redirect" 12 ) 13 14 // TODO: tests for popagating 4xx / 5xx, dont mask all these 15 // TODO: load _redirects relative to .Static.Dir? 16 // TODO: add list of methods to match on 17 18 // log context. 19 var ctx = logs.Plugin("redirects") 20 21 type rewrite struct { 22 http.ResponseWriter 23 header bool 24 ignore bool 25 } 26 27 // WriteHeader implementation. 28 func (r *rewrite) WriteHeader(code int) { 29 r.header = true 30 r.ignore = code == 404 31 if !r.ignore { 32 r.ResponseWriter.WriteHeader(code) 33 } 34 } 35 36 // Write implementation. 37 func (r *rewrite) Write(b []byte) (int, error) { 38 if r.ignore { 39 return len(b), nil 40 } 41 42 if !r.header { 43 r.WriteHeader(200) 44 return r.Write(b) 45 } 46 47 return r.ResponseWriter.Write(b) 48 } 49 50 // New redirects handler. 51 func New(c *up.Config, next http.Handler) (http.Handler, error) { 52 if len(c.Redirects) == 0 { 53 return next, nil 54 } 55 56 rules, err := redirect.Compile(c.Redirects) 57 if err != nil { 58 return nil, err 59 } 60 61 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 62 rule := rules.Lookup(r.URL.Path) 63 64 ctx := ctx.WithFields(log.Fields{ 65 "path": r.URL.Path, 66 }) 67 68 // pass-through 69 if rule == nil { 70 ctx.Debug("no match") 71 next.ServeHTTP(w, r) 72 return 73 } 74 75 // rewrite 76 if rule.IsRewrite() { 77 res := &rewrite{ResponseWriter: w} 78 next.ServeHTTP(res, r) 79 80 if res.ignore { 81 ctx.WithField("dest", r.URL.Path).Info("rewrite") 82 r.Header.Set("X-Original-Path", r.URL.Path) 83 r.URL.Path = rule.URL(r.URL.Path) 84 next.ServeHTTP(w, r) 85 } 86 return 87 } 88 89 // redirect 90 ctx.WithField("dest", r.URL.Path).Info("redirect") 91 w.Header().Set("Location", rule.URL(r.URL.Path)) 92 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 93 w.WriteHeader(rule.Status) 94 fmt.Fprintln(w, http.StatusText(rule.Status)) 95 }) 96 97 return h, nil 98 }