Repository: google/go-safeweb
Pull request #271 (2021-04-14 10:43:55)

Add support for go1.16 embed.FS.
Note that I bumped the go.mod file to use go 1.16 but made sure this
still works with go1.15.

This module will now conditionally build with different features
depending on the compiler version used. Expressing the 1.16 version in
go.mod only means this module *may* use newer features, but it can still
be backward compatible.

This is gonna be useful for any users that want to bundle static resources
in their web servers and doesn't break users on previous versions of Go.


Patch:
diff --git a/go.mod b/go.mod
index f0664bf..d40bacd 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/google/go-safeweb
 
-go 1.14
+go 1.16
 
 require (
 	github.com/google/go-cmp v0.5.0
diff --git a/safehttp/fileserver_1_16.go b/safehttp/fileserver_1_16.go
new file mode 100644
index 0000000..f081e57
--- /dev/null
+++ b/safehttp/fileserver_1_16.go
@@ -0,0 +1,31 @@
+// 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.
+
+// +build go1.16
+
+package safehttp
+
+import (
+	"embed"
+	"net/http"
+)
+
+func FileServerEmbed(fs embed.FS) Handler {
+	fileServer := http.FileServer(http.FS(fs))
+	return HandlerFunc(func(rw ResponseWriter, req *IncomingRequest) Result {
+		fsrw := &fileServerResponseWriter{flight: rw.(*flight), header: http.Header{}}
+		fileServer.ServeHTTP(fsrw, req.req)
+		return fsrw.result
+	})
+}


Test patch:
diff --git a/safehttp/fileserver_1_16_test.go b/safehttp/fileserver_1_16_test.go
new file mode 100644
index 0000000..65eea9c
--- /dev/null
+++ b/safehttp/fileserver_1_16_test.go
@@ -0,0 +1,91 @@
+// 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.
+
+// +build go1.16
+
+package safehttp_test
+
+import (
+	"embed"
+	"io"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-safeweb/safehttp"
+)
+
+//go:embed testdata
+var testEmbeddedFS embed.FS
+
+func TestFileServerEmbed(t *testing.T) {
+	wantFile, err := testEmbeddedFS.Open("testdata/embed.html")
+	if err != nil {
+		t.Fatalf("Could not open embedded test files: %v", err)
+	}
+	wantFileContent, err := io.ReadAll(wantFile)
+	if err != nil {
+		t.Fatalf("Could not read embedded test files: %v", err)
+	}
+
+	tests := []struct {
+		name     string
+		path     string
+		wantCode safehttp.StatusCode
+		wantCT   string
+		wantBody string
+	}{
+		{
+			name:     "missing file",
+			path:     "failure",
+			wantCode: 404,
+			wantCT:   "text/plain; charset=utf-8",
+			wantBody: "Not Found\n",
+		},
+		{
+			name:     "embedded file",
+			path:     "testdata/embed.html",
+			wantCode: 200,
+			wantCT:   "text/html; charset=utf-8",
+			wantBody: string(wantFileContent),
+		},
+	}
+
+	mb := &safehttp.ServeMuxConfig{}
+	mb.Handle("/", safehttp.MethodGet, safehttp.FileServerEmbed(testEmbeddedFS))
+	m := mb.Mux()
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			rr := httptest.NewRecorder()
+
+			req := httptest.NewRequest(safehttp.MethodGet, "https://test.science/"+tt.path, nil)
+			m.ServeHTTP(rr, req)
+
+			if got, want := rr.Result().StatusCode, tt.wantCode; safehttp.StatusCode(got) != tt.wantCode {
+				t.Errorf("status code got: %v want: %v", got, want)
+			}
+			if got := rr.Header().Get("Content-Type"); tt.wantCT != got {
+				t.Errorf("Content-Type: got %q want %q", got, tt.wantCT)
+			}
+			body, err := io.ReadAll(rr.Result().Body)
+			if err != nil {
+				t.Errorf("Can't read response body: %v", err)
+			}
+			if diff := cmp.Diff(tt.wantBody, string(body)); diff != "" {
+				t.Errorf("Response body diff (-want,+got): \n%s", diff)
+			}
+		})
+	}
+}
diff --git a/safehttp/testdata/embed.html b/safehttp/testdata/embed.html
new file mode 100644
index 0000000..356936c
--- /dev/null
+++ b/safehttp/testdata/embed.html
@@ -0,0 +1,19 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+  <body>
+    <h1> This is a test embedded document </h1>
+  </body>
+</html>