Repository: google/go-safeweb (go)

Issue:
Make it possible to register plugins (interceptors) on mux
We need to be able to install plugins on the `ServeMux` and they should then run when an request reaches the `ServeMux`.

Pull request:
Interceptors Infrastructure
Fixes #64 

Implemented the interceptors infrastructure that supports adding interceptors for `ServeMux`. To install an interceptor, the user needs to pass it to one of the installation functions, together with a key that will uniquely identify the interceptor and that the user will need to keep track of.  The `Interceptor.Before` function will run prior to accessing a handler for all the interceptors installed. Within the handler, interaction with an interceptor is possible by calling the `Interceptor()` getter function on the `ResponseWriter` and passing the interceptor key as a parameter to the function.

Patch:
diff --git a/go.sum b/go.sum
index af176cb..8692892 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,10 @@
 github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 github.com/google/safehtml v0.0.2 h1:ZOt2VXg4x24bW0m2jtzAOkhoXV0iM8vNKc0paByCZqM=
 github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/safehttp/interceptor.go b/safehttp/interceptor.go
new file mode 100644
index 0000000..c82a08c
--- /dev/null
+++ b/safehttp/interceptor.go
@@ -0,0 +1,25 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package safehttp
+
+// Interceptor can be installed on a ServeMux in order to apply its
+// functionality on an IncomingRequest before it is sent to its corresponding
+// handler.
+type Interceptor interface {
+	// Before runs before the IncomingRequest is sent to the handler. If a
+	// response is written to the ResponseWriter, then the remaining
+	// interceptors and the handler won't execute.
+	Before(w ResponseWriter, r *IncomingRequest) Result
+}
diff --git a/safehttp/mux.go b/safehttp/mux.go
index 76ddc98..35b7ffb 100644
--- a/safehttp/mux.go
+++ b/safehttp/mux.go
@@ -74,6 +74,9 @@ type ServeMux struct {
 
 	// Maps patterns to handlers supporting multiple HTTP methods.
 	handlers map[string]methodHandler
+
+	// Maps interceptor key to interceptor.
+	interceps map[string]Interceptor
 }
 
 // NewServeMux allocates and returns a new ServeMux
@@ -84,10 +87,11 @@ func NewServeMux(d Dispatcher, domains ...string) *ServeMux {
 		dm[host] = true
 	}
 	return &ServeMux{
-		mux:      http.NewServeMux(),
-		domains:  dm,
-		disp:     d,
-		handlers: map[string]methodHandler{},
+		mux:       http.NewServeMux(),
+		domains:   dm,
+		disp:      d,
+		handlers:  map[string]methodHandler{},
+		interceps: map[string]Interceptor{},
 	}
 }
 
@@ -97,9 +101,10 @@ func (m *ServeMux) Handle(pattern string, method string, h Handler) {
 	mh, ok := m.handlers[pattern]
 	if !ok {
 		mh := methodHandler{
-			handlers: map[string]Handler{method: h},
-			domains:  m.domains,
-			disp:     m.disp,
+			handlers:     map[string]Handler{method: h},
+			domains:      m.domains,
+			disp:         m.disp,
+			muxInterceps: m.interceps,
 		}
 
 		m.handlers[pattern] = mh
@@ -113,23 +118,41 @@ func (m *ServeMux) Handle(pattern string, method string, h Handler) {
 	mh.handlers[method] = h
 }
 
+// Install installs an Interceptor. Interceptor keys need to be unique. If an
+// Interceptor with the same key has already been installed, Install panics.
+//
+// TODO(@empijei, @grenfeldt, @kele, @mihalimara22): Right now you could install
+// the same interceptor twice with different keys, we need to figure out how
+// exactly we want to avoid that and how we define key uniqueness.
+func (m *ServeMux) Install(key string, i Interceptor) {
+	if _, exists := m.interceps[key]; exists {
+		panic("interceptor with same key already installed")
+	}
+	m.interceps[key] = i
+}
+
 // ServeHTTP dispatches the request to the handler whose method matches the
 // incoming request and whose pattern most closely matches the request URL.
 func (m *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	m.mux.ServeHTTP(w, r)
 }
 
-// methodHandler is collection of handlers based on the request method.
+// methodHandler is a collection of handlers based on the request method.
 type methodHandler struct {
 	// Maps an HTTP method to its handler
-	handlers map[string]Handler
-	domains  map[string]bool
-	disp     Dispatcher
+	handlers     map[string]Handler
+	domains      map[string]bool
+	disp         Dispatcher
+	muxInterceps map[string]Interceptor
 }
 
 // ServeHTTP dispatches the request to the handler associated with
-// the incoming request's method.
+// the incoming request's method after calling the Before function of all
+// ServeMux interceptors the handler is registered on.
 func (m methodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	rw := NewResponseWriter(m.disp, w, m.muxInterceps)
+	ir := NewIncomingRequest(r)
+
 	if !m.domains[r.Host] {
 		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
 		return
@@ -141,5 +164,11 @@ func (m methodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	h.ServeHTTP(NewResponseWriter(m.disp, w), NewIncomingRequest(r))
+	for _, intercep := range m.muxInterceps {
+		if res := intercep.Before(rw, ir); res.written {
+			return
+		}
+	}
+
+	h.ServeHTTP(rw, ir)
 }
diff --git a/safehttp/prototype.go b/safehttp/prototype.go
index 5729f94..f174bc1 100644
--- a/safehttp/prototype.go
+++ b/safehttp/prototype.go
@@ -31,7 +31,7 @@ func NewMachinery(h HandleFunc, d Dispatcher) *Machinery {
 
 // HandleRequest TODO
 func (m *Machinery) HandleRequest(w http.ResponseWriter, req *http.Request) {
-	rw := NewResponseWriter(m.d, w)
+	rw := NewResponseWriter(m.d, w, nil)
 	ir := NewIncomingRequest(req)
 	m.h(rw, ir)
 }
diff --git a/safehttp/response_writer.go b/safehttp/response_writer.go
index 1467141..2cc6d03 100644
--- a/safehttp/response_writer.go
+++ b/safehttp/response_writer.go
@@ -26,25 +26,42 @@ type ResponseWriter struct {
 	// Having this field unexported is essential for
 	// security. Otherwise one can easily overwrite
 	// the struct bypassing all our safety guarantees.
-	header Header
+	header       Header
+	muxInterceps map[string]Interceptor
 }
 
-// NewResponseWriter creates a ResponseWriter from
-// a safehttp.Dispatcher and a http.ResponseWriter.
-func NewResponseWriter(d Dispatcher, rw http.ResponseWriter) ResponseWriter {
+// NewResponseWriter creates a ResponseWriter from a safehttp.Dispatcher, an
+// http.ResponseWriter and a list of interceptors associated with a ServeMux.
+func NewResponseWriter(d Dispatcher, rw http.ResponseWriter, muxInterceps map[string]Interceptor) ResponseWriter {
 	header := newHeader(rw.Header())
-	return ResponseWriter{d: d, rw: rw, header: header}
+	return ResponseWriter{
+		d:            d,
+		rw:           rw,
+		header:       header,
+		muxInterceps: muxInterceps,
+	}
+}
+
+// Interceptor returns the interceptor associated with the given key.
+func (w *ResponseWriter) Interceptor(key string) Interceptor {
+	mp, ok := w.muxInterceps[key]
+	if !ok {
+		return nil
+	}
+	return mp
 }
 
 // Result TODO
-type Result struct{}
+type Result struct {
+	written bool
+}
 
 // Write TODO
 func (w *ResponseWriter) Write(resp Response) Result {
 	if err := w.d.Write(w.rw, resp); err != nil {
 		panic("error")
 	}
-	return Result{}
+	return Result{written: true}
 }
 
 // WriteTemplate TODO
@@ -52,7 +69,7 @@ func (w *ResponseWriter) WriteTemplate(t Template, data interface{}) Result {
 	if err := w.d.ExecuteTemplate(w.rw, t, data); err != nil {
 		panic("error")
 	}
-	return Result{}
+	return Result{written: true}
 }
 
 // ClientError TODO
@@ -62,7 +79,7 @@ func (w *ResponseWriter) ClientError(code StatusCode) Result {
 		panic("wrong method called")
 	}
 	http.Error(w.rw, http.StatusText(int(code)), int(code))
-	return Result{}
+	return Result{written: true}
 }
 
 // ServerError TODO
@@ -71,16 +88,16 @@ func (w *ResponseWriter) ServerError(code StatusCode) Result {
 		// TODO(@mattiasgrenfeldt, @mihalimara22, @kele, @empijei): Decide how it should
 		// be communicated to the user of the framework that they've called the wrong
 		// method.
-		return Result{}
+		return Result{written: true}
 	}
 	http.Error(w.rw, http.StatusText(int(code)), int(code))
-	return Result{}
+	return Result{written: true}
 }
 
 // Redirect responds with a redirect to a given url, using code as the status code.
 func (w *ResponseWriter) Redirect(r *IncomingRequest, url string, code StatusCode) Result {
 	http.Redirect(w.rw, r.req, url, int(code))
-	return Result{}
+	return Result{written: true}
 }
 
 // Header returns the collection of headers that will be set


Test patch:
diff --git a/safehttp/mux_test.go b/safehttp/mux_test.go
index d80dffc..541e226 100644
--- a/safehttp/mux_test.go
+++ b/safehttp/mux_test.go
@@ -12,63 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package safehttp
+package safehttp_test
 
 import (
-	"io"
 	"net/http"
 	"net/http/httptest"
 	"strings"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-safeweb/safehttp"
 	"github.com/google/safehtml"
-	"github.com/google/safehtml/template"
 )
 
-type testDispatcher struct{}
-
-func (testDispatcher) Write(rw http.ResponseWriter, resp Response) error {
-	switch x := resp.(type) {
-	case safehtml.HTML:
-		_, err := rw.Write([]byte(x.String()))
-		return err
-	default:
-		panic("not a safe response type")
-	}
-}
-
-func (testDispatcher) ExecuteTemplate(rw http.ResponseWriter, t Template, data interface{}) error {
-	switch x := t.(type) {
-	case *template.Template:
-		return x.Execute(rw, data)
-	default:
-		panic("not a safe response type")
-	}
-}
-
-type responseRecorder struct {
-	header http.Header
-	writer io.Writer
-	status int
-}
-
-func newResponseRecorder(w io.Writer) *responseRecorder {
-	return &responseRecorder{header: http.Header{}, writer: w, status: http.StatusOK}
-}
-
-func (r *responseRecorder) Header() http.Header {
-	return r.header
-}
-
-func (r *responseRecorder) WriteHeader(statusCode int) {
-	r.status = statusCode
-}
-
-func (r *responseRecorder) Write(data []byte) (int, error) {
-	return r.writer.Write(data)
-}
-
 func TestMuxOneHandlerOneRequest(t *testing.T) {
 	var test = []struct {
 		name       string
@@ -108,12 +64,12 @@ func TestMuxOneHandlerOneRequest(t *testing.T) {
 
 	for _, tt := range test {
 		t.Run(tt.name, func(t *testing.T) {
-			mux := NewServeMux(testDispatcher{}, "foo.com")
+			mux := safehttp.NewServeMux(testDispatcher{}, "foo.com")
 
-			h := HandlerFunc(func(w ResponseWriter, r *IncomingRequest) Result {
+			h := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
 				return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
 			})
-			mux.Handle("/", MethodGet, h)
+			mux.Handle("/", safehttp.MethodGet, h)
 
 			b := &strings.Builder{}
 			rw := newResponseRecorder(b)
@@ -139,7 +95,7 @@ func TestMuxServeTwoHandlers(t *testing.T) {
 	var tests = []struct {
 		name        string
 		req         *http.Request
-		hf          HandlerFunc
+		hf          safehttp.Handler
 		wantStatus  int
 		wantHeaders map[string][]string
 		wantBody    string
@@ -147,7 +103,7 @@ func TestMuxServeTwoHandlers(t *testing.T) {
 		{
 			name: "GET Handler",
 			req:  httptest.NewRequest("GET", "http://foo.com/bar", nil),
-			hf: HandlerFunc(func(w ResponseWriter, r *IncomingRequest) Result {
+			hf: safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
 				return w.Write(safehtml.HTMLEscaped("<h1>Hello World! GET</h1>"))
 			}),
 			wantStatus:  200,
@@ -157,7 +113,7 @@ func TestMuxServeTwoHandlers(t *testing.T) {
 		{
 			name: "POST Handler",
 			req:  httptest.NewRequest("POST", "http://foo.com/bar", nil),
-			hf: HandlerFunc(func(w ResponseWriter, r *IncomingRequest) Result {
+			hf: safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
 				return w.Write(safehtml.HTMLEscaped("<h1>Hello World! POST</h1>"))
 			}),
 			wantStatus:  200,
@@ -167,9 +123,9 @@ func TestMuxServeTwoHandlers(t *testing.T) {
 	}
 
 	d := testDispatcher{}
-	mux := NewServeMux(d, "foo.com")
-	mux.Handle("/bar", MethodGet, tests[0].hf)
-	mux.Handle("/bar", MethodPost, tests[1].hf)
+	mux := safehttp.NewServeMux(d, "foo.com")
+	mux.Handle("/bar", safehttp.MethodGet, tests[0].hf)
+	mux.Handle("/bar", safehttp.MethodPost, tests[1].hf)
 
 	for _, test := range tests {
 		b := &strings.Builder{}
@@ -191,12 +147,12 @@ func TestMuxServeTwoHandlers(t *testing.T) {
 
 func TestMuxHandleSameMethodTwice(t *testing.T) {
 	d := testDispatcher{}
-	mux := NewServeMux(d, "foo.com")
+	mux := safehttp.NewServeMux(d, "foo.com")
 
-	registeredHandler := HandlerFunc(func(w ResponseWriter, r *IncomingRequest) Result {
+	registeredHandler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
 		return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
 	})
-	mux.Handle("/bar", MethodGet, registeredHandler)
+	mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
 
 	defer func() {
 		if r := recover(); r == nil {
@@ -204,5 +160,153 @@ func TestMuxHandleSameMethodTwice(t *testing.T) {
 		}
 	}()
 
-	mux.Handle("/bar", MethodGet, registeredHandler)
+	mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
+}
+
+type setHeaderInterceptor struct {
+	name  string
+	value string
+}
+
+func (p setHeaderInterceptor) Before(w safehttp.ResponseWriter, _ *safehttp.IncomingRequest) safehttp.Result {
+	if err := w.Header().Set(p.name, p.value); err != nil {
+		return w.ServerError(safehttp.StatusInternalServerError)
+	}
+	return safehttp.Result{}
+}
+
+type internalErrorInterceptor struct{}
+
+func (internalErrorInterceptor) Before(w safehttp.ResponseWriter, _ *safehttp.IncomingRequest) safehttp.Result {
+	return w.ServerError(safehttp.StatusInternalServerError)
+}
+
+type claimHeaderInterceptor struct {
+	headerToClaim string
+	setValue      func([]string)
+}
+
+func (p *claimHeaderInterceptor) Before(w safehttp.ResponseWriter, _ *safehttp.IncomingRequest) safehttp.Result {
+	f, err := w.Header().Claim(p.headerToClaim)
+	if err != nil {
+		return w.ServerError(safehttp.StatusInternalServerError)
+	}
+	p.setValue = f
+	return safehttp.Result{}
+}
+
+func (p *claimHeaderInterceptor) SetHeader(value string) {
+	p.setValue([]string{value})
+}
+
+func TestMuxInterceptors(t *testing.T) {
+	tests := []struct {
+		name        string
+		mux         *safehttp.ServeMux
+		wantStatus  int
+		wantHeaders map[string][]string
+		wantBody    string
+	}{
+		{
+			name: "Install ServeMux Interceptor before handler registration",
+			mux: func() *safehttp.ServeMux {
+				mux := safehttp.NewServeMux(testDispatcher{}, "foo.com")
+				mux.Install("test", setHeaderInterceptor{
+					name:  "Foo",
+					value: "bar",
+				})
+
+				registeredHandler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
+					return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
+				})
+				mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
+				return mux
+			}(),
+			wantStatus:  200,
+			wantHeaders: map[string][]string{"Foo": {"bar"}},
+			wantBody:    "&lt;h1&gt;Hello World!&lt;/h1&gt;",
+		},
+		{
+			name: "Install ServeMux interceptor after handler registration",
+			mux: func() *safehttp.ServeMux {
+				mux := safehttp.NewServeMux(testDispatcher{}, "foo.com")
+
+				registeredHandler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
+					return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
+				})
+				mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
+
+				mux.Install("test", setHeaderInterceptor{
+					name:  "Foo",
+					value: "bar",
+				})
+				return mux
+			}(),
+			wantStatus:  200,
+			wantHeaders: map[string][]string{"Foo": {"bar"}},
+			wantBody:    "&lt;h1&gt;Hello World!&lt;/h1&gt;",
+		},
+		{
+			name: "Install Interrupting Interceptor",
+			mux: func() *safehttp.ServeMux {
+				mux := safehttp.NewServeMux(testDispatcher{}, "foo.com")
+				mux.Install("test", internalErrorInterceptor{})
+
+				registeredHandler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
+					return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
+				})
+				mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
+
+				return mux
+			}(),
+			wantStatus: 500,
+			wantHeaders: map[string][]string{
+				"Content-Type":           {"text/plain; charset=utf-8"},
+				"X-Content-Type-Options": {"nosniff"},
+			},
+			wantBody: "Internal Server Error\n",
+		},
+		{
+			name: "Handler Communication With ServeMux Interceptor",
+			mux: func() *safehttp.ServeMux {
+				mux := safehttp.NewServeMux(testDispatcher{}, "foo.com")
+				mux.Install("claim", &claimHeaderInterceptor{headerToClaim: "Foo"})
+
+				registeredHandler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
+					p := w.Interceptor("claim").(*claimHeaderInterceptor)
+					p.SetHeader("bar")
+					return w.Write(safehtml.HTMLEscaped("<h1>Hello World!</h1>"))
+				})
+				mux.Handle("/bar", safehttp.MethodGet, registeredHandler)
+
+				return mux
+			}(),
+			wantStatus:  200,
+			wantHeaders: map[string][]string{"Foo": {"bar"}},
+			wantBody:    "&lt;h1&gt;Hello World!&lt;/h1&gt;",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := &strings.Builder{}
+			rw := newResponseRecorder(b)
+
+			req := httptest.NewRequest("GET", "http://foo.com/bar", nil)
+
+			tt.mux.ServeHTTP(rw, req)
+
+			if rw.status != tt.wantStatus {
+				t.Errorf("rw.status: got %v want %v", rw.status, tt.wantStatus)
+			}
+
+			if diff := cmp.Diff(tt.wantHeaders, map[string][]string(rw.header)); diff != "" {
+				t.Errorf("rw.header mismatch (-want +got):\n%s", diff)
+			}
+
+			if got := b.String(); got != tt.wantBody {
+				t.Errorf("response body: got %q want %q", got, tt.wantBody)
+			}
+		})
+	}
 }
diff --git a/safehttp/recorder_dispatcher_test.go b/safehttp/recorder_dispatcher_test.go
index 76822d0..d17106c 100644
--- a/safehttp/recorder_dispatcher_test.go
+++ b/safehttp/recorder_dispatcher_test.go
@@ -56,6 +56,7 @@ type responseRecorder struct {
 
 func newResponseRecorder(w io.Writer) *responseRecorder {
 	return &responseRecorder{
+		header: http.Header{},
 		writer: w,
 		status: http.StatusOK,
 	}
diff --git a/safehttp/safehttptest/recorder.go b/safehttp/safehttptest/recorder.go
index 5222848..f3df891 100644
--- a/safehttp/safehttptest/recorder.go
+++ b/safehttp/safehttptest/recorder.go
@@ -61,7 +61,7 @@ func NewResponseRecorder() *ResponseRecorder {
 	return &ResponseRecorder{
 		rw:             rw,
 		b:              &b,
-		ResponseWriter: safehttp.NewResponseWriter(testDispatcher{}, rw),
+		ResponseWriter: safehttp.NewResponseWriter(testDispatcher{}, rw, nil),
 	}
 }
 
@@ -73,7 +73,7 @@ func NewResponseRecorderFromDispatcher(d safehttp.Dispatcher) *ResponseRecorder
 	return &ResponseRecorder{
 		rw:             rw,
 		b:              &b,
-		ResponseWriter: safehttp.NewResponseWriter(d, rw),
+		ResponseWriter: safehttp.NewResponseWriter(d, rw, nil),
 	}
 }