Skip to content

Commit

Permalink
Overhauled how static-resources are served
Browse files Browse the repository at this point in the history
Rather than having a single handler for JS files, another
handler for CSS files, etc, we now have one unified handler
which is invoked for all static-resources.

We achieve this by adding a 404-handler, which first of all
checks whether the incoming-request can be satisfied via the
contents of our static-resources.  If so it will be served,
with an appropriate MIME-type, if not then a real 404
result will be returned to the caller.

The intention behind this change is that in the future we
can embed arbitrary (additional) static-resources without
the need to change our code.
  • Loading branch information
Steve Kemp committed Aug 19, 2020
1 parent dd818c0 commit 79ba823
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 147 deletions.
113 changes: 19 additions & 94 deletions cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"fmt"
"html/template"
"io/ioutil"
"mime"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -823,108 +824,34 @@ func NodeHandler(res http.ResponseWriter, req *http.Request) {
}
}

//
// IconHandler is the handler for the HTTP end-point
//
// GET /favicon.ico
//
// It will server an embedded binary resource.
//
func IconHandler(res http.ResponseWriter, req *http.Request) {

serveStatic(res, req, "data/favicon.ico", "image/vnd.microsoft.icon")
}

// CSSPath is the handler for all the CSS files beneath /css.
func CSSPath(res http.ResponseWriter, req *http.Request) {
func StaticHandler(res http.ResponseWriter, req *http.Request) {

//
// Get the path we're going to serve.
//
vars := mux.Vars(req)
path := vars["path"]
path := req.URL.Path

//
// Ensure we received a path.
// Is this a static-resource we know about?
//
if len(path) < 1 {
res.WriteHeader(http.StatusNotFound)
fmt.Fprint(res, "The request you made pointed to a missing resource")
return
}

//
// Serve it
//
serveStatic(res, req, "data/css/"+path, "text/css")
}

// serveStatic serves a static path, with the given MIME type.
func serveStatic(res http.ResponseWriter, req *http.Request, path string, mime string) {

// Load the asset.
data, err := getResource(path)
data, err := getResource("data" + path)
if err != nil {
res.WriteHeader(http.StatusNotFound)
fmt.Fprintf(res, "Error loading the resource you requested: %s", err.Error())
fmt.Fprintf(res, "Error loading the resource you requested: %s : %s", path, err.Error())
return
}

res.Header().Set("Content-Type", mime)
res.Write(data)
}

//
// JavascriptPath is the handler for all the javascript files beneath /js.
// It will serve an embedded javascript resource.
//
func JavascriptPath(res http.ResponseWriter, req *http.Request) {

//
// Get the path we're going to serve.
// OK at this point we're handling a valid static-resource,
// so we just need to get the content-type setup appropriately.
//
vars := mux.Vars(req)
path := vars["path"]

//
// Ensure we received a path.
//
if len(path) < 1 {
res.WriteHeader(http.StatusNotFound)
fmt.Fprint(res, "The request you made pointed to a missing resource")
return
}

//
// Serve it
//
serveStatic(res, req, "data/js/"+path, "application/javascript")
}

//
// FontsPath is the handler for all the font files beneath /fonts.
//
func FontsPath(res http.ResponseWriter, req *http.Request) {

//
// Get the path we're going to serve.
//
vars := mux.Vars(req)
path := vars["path"]

//
// Ensure we received a path.
//
if len(path) < 1 {
res.WriteHeader(http.StatusNotFound)
fmt.Fprint(res, "The request you made pointed to a missing resource")
return
suffix := filepath.Ext(path)
mType := mime.TypeByExtension(suffix)
if mType != "" {
res.Header().Set("Content-Type", mType)
}
res.Write(data)

//
// Serve it
//
serveStatic(res, req, "data/fonts/"+path, "font/woff2")
}

//
Expand Down Expand Up @@ -1083,6 +1010,12 @@ func serve(settings serveCmd) {
//
router := mux.NewRouter()

//
// Static-Files are handled via the 404-handler,
// as that is invoked when other routes don't match.
//
router.NotFoundHandler = http.HandlerFunc(StaticHandler)

//
// API end-points
//
Expand Down Expand Up @@ -1127,14 +1060,6 @@ func serve(settings serveCmd) {
router.HandleFunc("/environment/{environment}/", IndexHandler).Methods("GET")
router.HandleFunc("/environment/{environment}", IndexHandler).Methods("GET")

//
// Static-Files
//
router.HandleFunc("/favicon.ico", IconHandler).Methods("GET")
router.HandleFunc("/js/{path}", JavascriptPath).Methods("GET")
router.HandleFunc("/fonts/{path}", FontsPath).Methods("GET")
router.HandleFunc("/css/{path}", CSSPath).Methods("GET")

//
// Bind the router.
//
Expand Down
108 changes: 57 additions & 51 deletions cmd_serve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -840,70 +840,76 @@ func TestIndexView(t *testing.T) {
}

//
// Our icon is correct.
// Test that static-resources work:
//
func TestFavIcon(t *testing.T) {
// 1. They produce content
// 2. They have sensible MIME-types
//
func TestStaticResources(t *testing.T) {

// Test-cases
type TestCase struct {
path string
mime string
}

tests := []TestCase{
TestCase{path: "/favicon.ico", mime: "image/vnd.microsoft.icon"},
TestCase{path: "/robots.txt", mime: "text/plain"},
TestCase{path: "/js/jquery.tablesorter.min.js", mime: "application/javascript"},
TestCase{path: "/css/bootstrap.min.css", mime: "text/css"},
TestCase{path: "/fonts/glyphicons-halflings-regular.woff2", mime: "font/woff2"},
}

// Wire up the router.
r := mux.NewRouter()
r.HandleFunc("/favicon.ico", IconHandler).Methods("GET")
r.NotFoundHandler = http.HandlerFunc(StaticHandler)

// Get the test-server
ts := httptest.NewServer(r)
defer ts.Close()
for _, test := range tests {

//
// Get the icon
//
url := ts.URL + "/favicon.ico"
// Get the test-server
ts := httptest.NewServer(r)
defer ts.Close()

resp, err := http.Get(url)
if err != nil {
t.Fatal(err)
}
// Make a request
url := ts.URL + test.path

//
// Get the body
//
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
resp, err := http.Get(url)
if err != nil {
t.Fatal(err)
}

if err != nil {
t.Errorf("Failed to read response-body %v\n", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)

//
// Test the size is that we expect.
//
if len(body) != 1150 {
t.Errorf("Icon was the wrong size %v\n", len(body))
}
if err != nil {
t.Errorf("Failed to read response-body %v\n", err)
}

//
// Test that the content-type was what we expect.
//
headers := resp.Header
ctype := headers["Content-Type"][0]
if ctype != "image/vnd.microsoft.icon" {
t.Errorf("content type header does not match: got %v", ctype)
}
if len(body) < 10 {
t.Errorf("too-short body reading %s: %d\n", test.path, len(body))
}

//
// Now test we were served the data we expect.
//
// Load the resource
//
tmpl, err := getResource("data/favicon.ico")
if err != nil {
t.Fatal(err)
}
//
// Test that the content-type was what we expect.
//
headers := resp.Header
ctype := headers["Content-Type"][0]

//
// Compare byte by byte
//
for _, b := range tmpl {
if body[b] != tmpl[b] {
t.Errorf("favicon.ico content is corrupt?")
// Content-type might have a character-set, so we can
// expect either of these:
//
// Content-Type: text/plain
// Content-Type: text/css; charset=utf-8
//
// Strip anything after the ";" to avoid caring about this
if strings.Contains(ctype, ";") {
pieces := strings.Split(ctype, ";")
ctype = pieces[0]
}

if ctype != test.mime {
t.Errorf("expected %s for %s - got %s", test.mime, test.path, ctype)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions data/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *
Disallow: /
6 changes: 6 additions & 0 deletions static.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ var RESOURCES = map[string]EmbeddedResource{
Length: 4815,
},

"data/robots.txt": {
Filename: "data/robots.txt",
Contents: "H4sIAAAAAAAC/wotTi3STUxPzSuxUtDicsksTszJyS+3UtDnAgQAAP//QoSkjxoAAAA=",
Length: 26,
},

"data/valid.yaml": {
Filename: "data/valid.yaml",
Contents: "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",
Expand Down
4 changes: 2 additions & 2 deletions static_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
// Test that we have one embedded resource.
//
func TestResourceCount(t *testing.T) {
expected := 13
expected := 14
out := getResources()

if len(out) != expected {
t.Errorf("We expected %d resources but found %d.", expected, len(out))
}
Expand Down

0 comments on commit 79ba823

Please sign in to comment.