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
This commit is contained in:
Jeff Hodges 2015-09-22 20:40:05 -07:00
parent 7a10b3c546
commit 601cf9f0fb
4 changed files with 72 additions and 2 deletions

View File

@ -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,
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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"