Skip to content

Commit

Permalink
Add HTTP header support for the dev server
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Mar 8, 2020
1 parent cb12f41 commit c48e620
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
6 changes: 6 additions & 0 deletions commands/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
return nil, "", "", errors.Wrap(err, "Invalid baseURL")
}

serverConfig := config.DecodeServer(f.c.Cfg)

decorate := func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if f.c.showErrorInBrowser {
Expand Down Expand Up @@ -355,6 +357,10 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
w.Header().Set("Pragma", "no-cache")
}

for _, header := range serverConfig.Match(r.RequestURI) {
w.Header().Set(header.Key, header.Value)
}

if f.c.fastRenderMode && f.c.buildErr == nil {
p := r.RequestURI
if strings.HasSuffix(p, "/") || strings.HasSuffix(p, "html") || strings.HasSuffix(p, "htm") {
Expand Down
59 changes: 59 additions & 0 deletions config/commonConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@
package config

import (
"sort"
"strings"
"sync"

"github.com/gohugoio/hugo/common/types"

"github.com/gobwas/glob"
"github.com/gohugoio/hugo/common/herrors"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"
Expand Down Expand Up @@ -88,3 +93,57 @@ func DecodeSitemap(prototype Sitemap, input map[string]interface{}) Sitemap {

return prototype
}

// Config for the dev server.
type Server struct {
Headers []Headers

compiledInit sync.Once
compiled []glob.Glob
}

func (s Server) Match(pattern string) []types.KeyValueStr {
s.compiledInit.Do(func() {
for _, h := range s.Headers {
s.compiled = append(s.compiled, glob.MustCompile(h.For))
}
})

if s.compiled == nil {
return nil
}

var matches []types.KeyValueStr

for i, g := range s.compiled {
if g.Match(pattern) {
h := s.Headers[i]
for k, v := range h.Values {
matches = append(matches, types.KeyValueStr{k, cast.ToString(v)})
}
}
}

sort.Slice(matches, func(i, j int) bool {
return matches[i].Key < matches[j].Key
})

return matches

}

type Headers struct {
For string
Values map[string]interface{}
}

func DecodeServer(cfg Provider) Server {
m := cfg.GetStringMap("server")
var s Server
if m == nil {
return s
}

_ = mapstructure.WeakDecode(m, &s)
return s
}
24 changes: 24 additions & 0 deletions config/commonConfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"testing"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/types"

qt "github.com/frankban/quicktest"

Expand Down Expand Up @@ -58,3 +59,26 @@ func TestBuild(t *testing.T) {
c.Assert(b.UseResourceCache(nil), qt.Equals, false)

}

func TestServer(t *testing.T) {
c := qt.New(t)

cfg, err := FromConfigString(`[[server.headers]]
for = "/*.jpg"
[server.headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
`, "toml")

c.Assert(err, qt.IsNil)

s := DecodeServer(cfg)

c.Assert(s.Match("/foo.jpg"), qt.DeepEquals, []types.KeyValueStr{
{Key: "X-Content-Type-Options", Value: "nosniff"},
{Key: "X-Frame-Options", Value: "DENY"},
{Key: "X-XSS-Protection", Value: "1; mode=block"}})

}

0 comments on commit c48e620

Please sign in to comment.