http/headers/headers.go

Summary

Maintainability
A
0 mins
Test Coverage
// Package headers provides header injection support.
package headers

import (
    "net/http"
    "os"

    "github.com/apex/log"
    "github.com/pkg/errors"
    hdr "github.com/tj/go-headers"

    "github.com/apex/up"
    "github.com/apex/up/internal/header"
)

// TODO: document precedence and/or add options
// TODO: maybe allow storing _headers in Static.Dir?

// filename of headers file.
var filename = "_headers"

// New headers handler.
func New(c *up.Config, next http.Handler) (http.Handler, error) {
    rulesFromFile, err := readFromFile(filename)
    if err != nil {
        return nil, errors.Wrap(err, "reading header file")
    }

    rules, err := header.Compile(header.Merge(rulesFromFile, c.Headers))
    if err != nil {
        return nil, errors.Wrap(err, "compiling header")
    }

    log.Debugf("header rules from _headers file: %d", len(rulesFromFile))
    log.Debugf("header rules from up.json: %d", len(c.Headers))

    h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fields := rules.Lookup(r.URL.Path)

        for k, v := range fields {
            w.Header().Set(k, v)
        }

        next.ServeHTTP(w, r)
    })

    return h, nil
}

// readFromFile reads from a Netlify style headers file.
func readFromFile(path string) (header.Rules, error) {
    rules := make(header.Rules)

    f, err := os.Open(path)

    if os.IsNotExist(err) {
        return nil, nil
    }

    if err != nil {
        return nil, errors.Wrap(err, "opening headers file")
    }

    defer f.Close()

    h, err := hdr.Parse(f)
    if err != nil {
        return nil, errors.Wrap(err, "parsing")
    }

    for path, fields := range h {
        rules[path] = make(header.Fields)
        for name, vals := range fields {
            rules[path][name] = vals[0]
        }
    }

    return rules, nil
}