github.com/hhsnopek/up@v0.1.1/http/headers/headers.go (about)

     1  // Package headers provides header injection support.
     2  package headers
     3  
     4  import (
     5  	"net/http"
     6  	"os"
     7  
     8  	"github.com/apex/log"
     9  	"github.com/pkg/errors"
    10  	hdr "github.com/tj/go-headers"
    11  
    12  	"github.com/apex/up"
    13  	"github.com/apex/up/internal/header"
    14  )
    15  
    16  // TODO: document precedence and/or add options
    17  // TODO: maybe allow storing _headers in Static.Dir?
    18  
    19  // filename of headers file.
    20  var filename = "_headers"
    21  
    22  // Headers handler injects headers.
    23  type Headers struct {
    24  	next  http.Handler
    25  	rules *header.Matcher
    26  }
    27  
    28  // New headers handler.
    29  func New(c *up.Config, next http.Handler) (http.Handler, error) {
    30  	rulesFromFile, err := readFromFile(filename)
    31  	if err != nil {
    32  		return nil, errors.Wrap(err, "reading header file")
    33  	}
    34  
    35  	rules, err := header.Compile(header.Merge(rulesFromFile, c.Headers))
    36  	if err != nil {
    37  		return nil, errors.Wrap(err, "compiling header")
    38  	}
    39  
    40  	log.Debugf("header rules from _headers file: %d", len(rulesFromFile))
    41  	log.Debugf("header rules from up.json: %d", len(c.Headers))
    42  
    43  	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    44  		fields := rules.Lookup(r.URL.Path)
    45  
    46  		for k, v := range fields {
    47  			w.Header().Set(k, v)
    48  		}
    49  
    50  		next.ServeHTTP(w, r)
    51  	})
    52  
    53  	return h, nil
    54  }
    55  
    56  // readFromFile reads from a Netlify style headers file.
    57  func readFromFile(path string) (header.Rules, error) {
    58  	rules := make(header.Rules)
    59  
    60  	f, err := os.Open(path)
    61  
    62  	if os.IsNotExist(err) {
    63  		return nil, nil
    64  	}
    65  
    66  	if err != nil {
    67  		return nil, errors.Wrap(err, "opening headers file")
    68  	}
    69  
    70  	defer f.Close()
    71  
    72  	h, err := hdr.Parse(f)
    73  	if err != nil {
    74  		return nil, errors.Wrap(err, "parsing")
    75  	}
    76  
    77  	for path, fields := range h {
    78  		rules[path] = make(header.Fields)
    79  		for name, vals := range fields {
    80  			rules[path][name] = vals[0]
    81  		}
    82  	}
    83  
    84  	return rules, nil
    85  }