From 601cf9f0fb0b35e2499422ce8151a63565c481b9 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 22 Sep 2015 20:40:05 -0700 Subject: [PATCH] add Cache-Control headers to ocsp-responder Also, adds a JSONDuration to clean up some of the config code. It will get used more in later PRs. Fixes #797 --- cmd/ocsp-responder/main.go | 21 +++++++++++++++++++-- cmd/ocsp-responder/main_test.go | 25 +++++++++++++++++++++++++ cmd/shell.go | 27 +++++++++++++++++++++++++++ test/boulder-config.json | 1 + 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go index 58390b65a..02e1c6724 100644 --- a/cmd/ocsp-responder/main.go +++ b/cmd/ocsp-responder/main.go @@ -27,6 +27,16 @@ import ( "github.com/letsencrypt/boulder/sa" ) +type cacheCtrlHandler struct { + http.Handler + MaxAge time.Duration +} + +func (c *cacheCtrlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", c.MaxAge/time.Second)) + c.Handler.ServeHTTP(w, r) +} + /* DBSource maps a given Database schema to a CA Key Hash, so we can pick from among them when presented with OCSP requests for different certs. @@ -158,9 +168,9 @@ func main() { killTimeout, err := time.ParseDuration(c.OCSPResponder.ShutdownKillTimeout) cmd.FailOnError(err, "Couldn't parse shutdown kill timeout") - // Configure HTTP m := http.NewServeMux() - m.Handle(c.OCSPResponder.Path, cfocsp.Responder{Source: source}) + m.Handle(c.OCSPResponder.Path, + handler(source, c.OCSPResponder.MaxAge.Duration)) httpMonitor := metrics.NewHTTPMonitor(stats, m, "OCSP") srv := &http.Server{ @@ -179,3 +189,10 @@ func main() { app.Run() } + +func handler(src cfocsp.Source, maxAge time.Duration) http.Handler { + return &cacheCtrlHandler{ + Handler: cfocsp.Responder{Source: src}, + MaxAge: maxAge, + } +} diff --git a/cmd/ocsp-responder/main_test.go b/cmd/ocsp-responder/main_test.go index 06ab7d0f9..19e781702 100644 --- a/cmd/ocsp-responder/main_test.go +++ b/cmd/ocsp-responder/main_test.go @@ -1 +1,26 @@ package main + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp" +) + +func TestCacheControl(t *testing.T) { + src := make(ocsp.InMemorySource) + h := handler(src, 10*time.Second) + w := httptest.NewRecorder() + r, err := http.NewRequest("GET", "/", nil) + if err != nil { + t.Fatal(err) + } + h.ServeHTTP(w, r) + expected := "max-age=10" + actual := w.Header().Get("Cache-Control") + if actual != expected { + t.Errorf("Cache-Control value: want %#v, got %#v", expected, actual) + } +} diff --git a/cmd/shell.go b/cmd/shell.go index 9e1b0b423..6d2636e0a 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -159,6 +159,9 @@ type Config struct { Path string ListenAddress string + // MaxAge is the max-age to set in the Cache-Controler response + // header. It is a time.Duration formatted string. + MaxAge JSONDuration ShutdownStopTimeout string ShutdownKillTimeout string @@ -392,3 +395,27 @@ func DebugServer(addr string) { log.Printf("booting debug server at %#v", addr) log.Println(http.Serve(ln, nil)) } + +type JSONDuration struct { + time.Duration +} + +var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a JSONDuration") + +func (d *JSONDuration) UnmarshalJSON(b []byte) error { + s := "" + err := json.Unmarshal(b, &s) + if err != nil { + if _, ok := err.(*json.UnmarshalTypeError); ok { + return ErrDurationMustBeString + } + return err + } + dd, err := time.ParseDuration(s) + d.Duration = dd + return err +} + +func (d JSONDuration) MarshalJSON() ([]byte, error) { + return []byte(d.Duration.String()), nil +} diff --git a/test/boulder-config.json b/test/boulder-config.json index 1a4da2ed0..10692373f 100644 --- a/test/boulder-config.json +++ b/test/boulder-config.json @@ -148,6 +148,7 @@ "source": "mysql+tcp://boulder@localhost:3306/boulder_sa_integration", "path": "/", "listenAddress": "localhost:4002", + "maxAge": "10s", "shutdownStopTimeout": "10s", "shutdownKillTimeout": "1m", "debugAddr": "localhost:8005"