From d6fbc25111a9a76b40f19b2481428a9170c706b0 Mon Sep 17 00:00:00 2001 From: Ben Morrison Date: Mon, 13 May 2019 17:20:40 -0400 Subject: serving css virtually instead of directly --- assets/style.css | 1 + handlers.go | 49 ++++++++++++++++++++++++++++++++++++++++++------- handlers_test.go | 26 ++++++++++++++++++++++++++ main.go | 3 +++ types.go | 5 +++-- 5 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 assets/style.css diff --git a/assets/style.css b/assets/style.css new file mode 100644 index 0000000..e148bda --- /dev/null +++ b/assets/style.css @@ -0,0 +1 @@ +@import url("https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css"); diff --git a/handlers.go b/handlers.go index 01049b0..3b23761 100644 --- a/handlers.go +++ b/handlers.go @@ -3,8 +3,10 @@ package main import ( "crypto/sha256" "fmt" + "io/ioutil" "log" "net/http" + "os" "time" "github.com/gorilla/mux" @@ -15,7 +17,7 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", htmlutf8) n, err := w.Write([]byte("getwtxt v" + getwtxt)) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -33,7 +35,7 @@ func apiBaseHandler(w http.ResponseWriter, r *http.Request) { timerfc3339 = append(timerfc3339, pathdata...) n, err := w.Write(timerfc3339) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -46,7 +48,7 @@ func apiFormatHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", txtutf8) n, err := w.Write([]byte(format + "\n")) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -59,7 +61,7 @@ func apiEndpointHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", htmlutf8) n, err := w.Write([]byte(format + "/" + endpoint)) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -73,7 +75,7 @@ func apiEndpointPOSTHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", htmlutf8) n, err := w.Write([]byte(format + "/" + endpoint)) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -86,7 +88,7 @@ func apiTagsBaseHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", htmlutf8) n, err := w.Write([]byte("api/" + format + "/tags")) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } @@ -100,7 +102,40 @@ func apiTagsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", htmlutf8) n, err := w.Write([]byte("api/" + format + "/tags/" + tags)) if err != nil || n == 0 { - log.Printf("Error writing to HTTP stream: %v\n", err) + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) } } + +// Serving the stylesheet virtually because +// files aren't served directly. +func cssHandler(w http.ResponseWriter, _ *http.Request) { + // read the raw bytes of the stylesheet + css, err := ioutil.ReadFile("assets/style.css") + if err != nil { + if os.IsNotExist(err) { + log.Printf("CSS file does not exist: /css request 404\n") + http.Error(w, err.Error(), http.StatusNotFound) + return + } + log.Printf("%v\n", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Get the mod time for the etag header + stat, err := os.Stat("assets/style.css") + if err != nil { + log.Printf("Couldn't stat CSS file to send ETag header: %v\n", err) + } + + // Sending the sha256 sum of the modtime in hexadecimal for the ETag header + etag := fmt.Sprintf("%x", sha256.Sum256([]byte(stat.ModTime().String()))) + + w.Header().Set("ETag", "\""+etag+"\"") + w.Header().Set("Content-Type", cssutf8) + n, err := w.Write(css) + if err != nil || n == 0 { + log.Printf("Error writing to HTTP stream: %v bytes, %v\n", n, err) + } +} diff --git a/handlers_test.go b/handlers_test.go index 66c1566..4ae7ae6 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -1,7 +1,9 @@ package main import ( + "bytes" "fmt" + "io/ioutil" "log" "net/http" "net/http/httptest" @@ -98,3 +100,27 @@ func Test_apiTagsHandler(t *testing.T) { } }) } +func Test_cssHandler(t *testing.T) { + initTestConf() + + name := "CSS Handler Test" + css, err := ioutil.ReadFile("assets/style.css") + if err != nil { + t.Errorf("Couldn't read assets/style.css: %v\n", err) + } + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "localhost"+testport+"/css", nil) + + t.Run(name, func(t *testing.T) { + cssHandler(w, req) + resp := w.Result() + body, _ := ioutil.ReadAll(resp.Body) + if resp.StatusCode != 200 { + t.Errorf("cssHandler(): %v\n", resp.StatusCode) + } + if !bytes.Equal(body, css) { + t.Errorf("cssHandler(): Byte mismatch\n") + } + }) +} diff --git a/main.go b/main.go index bca7847..8e3a2b2 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,9 @@ func main() { index.Path("/"). Methods("GET"). HandlerFunc(indexHandler) + index.Path("/css"). + Methods("GET"). + HandlerFunc(cssHandler) index.Path("/api"). Methods("GET"). HandlerFunc(apiBaseHandler) diff --git a/types.go b/types.go index 35f70b3..c7d1c50 100644 --- a/types.go +++ b/types.go @@ -1,8 +1,9 @@ package main // content-type consts -const txtutf8 = "text/plain; charset=utf8" -const htmlutf8 = "text/html; charset=utf8" +const txtutf8 = "text/plain; charset=utf-8" +const htmlutf8 = "text/html; charset=utf-8" +const cssutf8 = "text/css; charset=utf-8" // config object definition type configuration struct { -- cgit 1.4.1-2-gfad0