github.com/suedadam/up@v0.1.12/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 // destination path 76 path := rule.URL(r.URL.Path) 77 78 // forced rewrite 79 if rule.IsRewrite() && rule.Force { 80 ctx.WithField("dest", path).Info("forced rewrite") 81 r.Header.Set("X-Original-Path", r.URL.Path) 82 r.URL.Path = path 83 next.ServeHTTP(w, r) 84 return 85 } 86 87 // rewrite 88 if rule.IsRewrite() { 89 res := &rewrite{ResponseWriter: w} 90 next.ServeHTTP(res, r) 91 92 if res.ignore { 93 ctx.WithField("dest", path).Info("rewrite") 94 r.Header.Set("X-Original-Path", r.URL.Path) 95 r.URL.Path = path 96 next.ServeHTTP(w, r) 97 } 98 return 99 } 100 101 // redirect 102 ctx.WithField("dest", path).Info("redirect") 103 w.Header().Set("Location", path) 104 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 105 w.WriteHeader(rule.Status) 106 fmt.Fprintln(w, http.StatusText(rule.Status)) 107 }) 108 109 return h, nil 110 }