github.com/skabbes/up@v0.2.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  		// 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  }