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  }