Commit ac409d67 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Add caching and serving precompiled assets

parent afcae9ed
......@@ -86,11 +86,15 @@ var httpRoutes = [...]httpRoute{
httpRoute{"", regexp.MustCompile(ciAPIPattern), proxyRequest},
// Serve static files or forward the requests
httpRoute{"", nil, handleServeFile(documentRoot,
handleDeployPage(documentRoot,
handleRailsError(documentRoot,
proxyRequest,
)))},
httpRoute{"", nil,
handleServeFile(documentRoot, CacheDisabled,
handleDeployPage(documentRoot,
handleRailsError(documentRoot,
proxyRequest,
),
),
),
},
}
func main() {
......
......@@ -6,9 +6,17 @@ import (
"os"
"path/filepath"
"strings"
"time"
)
func handleServeFile(documentRoot *string, notFoundHandler serviceHandleFunc) serviceHandleFunc {
type CacheMode int
const (
CacheDisabled CacheMode = iota
CacheExpireMax
)
func handleServeFile(documentRoot *string, cache CacheMode, notFoundHandler serviceHandleFunc) serviceHandleFunc {
return func(w http.ResponseWriter, r *gitRequest) {
file := filepath.Join(*documentRoot, r.relativeURIPath)
......@@ -22,7 +30,22 @@ func handleServeFile(documentRoot *string, notFoundHandler serviceHandleFunc) se
return
}
content, fi, err := openFile(file)
var content *os.File
var fi os.FileInfo
var err error
// Serve pre-gzipped assets
if acceptEncoding := r.Header.Get("Accept-Encoding"); strings.Contains(acceptEncoding, "gzip") {
content, fi, err = openFile(file + ".gz")
if err == nil {
w.Header().Set("Content-Encoding", "gzip")
}
}
// If not found open the file
if content == nil || err != nil {
content, fi, err = openFile(file)
}
if err != nil {
if notFoundHandler != nil {
notFoundHandler(w, r)
......@@ -33,7 +56,15 @@ func handleServeFile(documentRoot *string, notFoundHandler serviceHandleFunc) se
}
defer content.Close()
log.Printf("StaticFile: serving %q", file)
switch cache {
case CacheExpireMax:
// Cache statically served files for 1 year
cacheUntil := time.Now().AddDate(1, 0, 0).Format(http.TimeFormat)
w.Header().Set("Cache-Control", "public")
w.Header().Set("Expires", cacheUntil)
}
log.Printf("StaticFile: serving %q %q", file, w.Header().Get("Content-Encoding"))
http.ServeContent(w, r.Request, filepath.Base(file), fi.ModTime(), content)
}
}
package main
import (
"bytes"
"compress/gzip"
"io/ioutil"
"net/http"
"net/http/httptest"
......@@ -11,12 +13,14 @@ import (
func TestServingNonExistingFile(t *testing.T) {
dir := "/path/to/non/existing/directory"
httpRequest, _ := http.NewRequest("GET", "/file", nil)
request := &gitRequest{
Request: httpRequest,
relativeURIPath: "/static/file",
}
w := httptest.NewRecorder()
handleServeFile(&dir, nil)(w, request)
handleServeFile(&dir, CacheDisabled, nil)(w, request)
assertResponseCode(t, w, 404)
}
......@@ -27,34 +31,40 @@ func TestServingDirectory(t *testing.T) {
}
defer os.RemoveAll(dir)
httpRequest, _ := http.NewRequest("GET", "/file", nil)
request := &gitRequest{
Request: httpRequest,
relativeURIPath: "/",
}
w := httptest.NewRecorder()
handleServeFile(&dir, nil)(w, request)
handleServeFile(&dir, CacheDisabled, nil)(w, request)
assertResponseCode(t, w, 404)
}
func TestServingMalformedUri(t *testing.T) {
dir := "/path/to/non/existing/directory"
httpRequest, _ := http.NewRequest("GET", "/file", nil)
request := &gitRequest{
Request: httpRequest,
relativeURIPath: "/../../../static/file",
}
w := httptest.NewRecorder()
handleServeFile(&dir, nil)(w, request)
handleServeFile(&dir, CacheDisabled, nil)(w, request)
assertResponseCode(t, w, 500)
}
func TestExecutingHandlerWhenNoFileFound(t *testing.T) {
dir := "/path/to/non/existing/directory"
httpRequest, _ := http.NewRequest("GET", "/file", nil)
request := &gitRequest{
Request: httpRequest,
relativeURIPath: "/static/file",
}
executed := false
handleServeFile(&dir, func(w http.ResponseWriter, r *gitRequest) {
handleServeFile(&dir, CacheDisabled, func(w http.ResponseWriter, r *gitRequest) {
executed = (r == request)
})(nil, request)
if !executed {
......@@ -79,9 +89,61 @@ func TestServingTheActualFile(t *testing.T) {
ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
w := httptest.NewRecorder()
handleServeFile(&dir, nil)(w, request)
handleServeFile(&dir, CacheDisabled, nil)(w, request)
assertResponseCode(t, w, 200)
if w.Body.String() != fileContent {
t.Error("We should serve the file: ", w.Body.String())
}
}
func testServingThePregzippedFile(t *testing.T, enableGzip bool) {
dir, err := ioutil.TempDir("", "deploy")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
httpRequest, _ := http.NewRequest("GET", "/file", nil)
request := &gitRequest{
Request: httpRequest,
relativeURIPath: "/file",
}
if enableGzip {
httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
}
fileContent := "STATIC"
var fileGzipContent bytes.Buffer
fileGzip := gzip.NewWriter(&fileGzipContent)
fileGzip.Write([]byte(fileContent))
fileGzip.Close()
ioutil.WriteFile(filepath.Join(dir, "file.gz"), fileGzipContent.Bytes(), 0600)
ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
w := httptest.NewRecorder()
handleServeFile(&dir, CacheDisabled, nil)(w, request)
assertResponseCode(t, w, 200)
if enableGzip {
assertResponseHeader(t, w, "Content-Encoding", "gzip")
if bytes.Compare(w.Body.Bytes(), fileGzipContent.Bytes()) != 0 {
t.Error("We should serve the pregzipped file")
}
} else {
assertResponseCode(t, w, 200)
assertResponseHeader(t, w, "Content-Encoding", "")
if w.Body.String() != fileContent {
t.Error("We should serve the file: ", w.Body.String())
}
}
}
func TestServingThePregzippedFile(t *testing.T) {
testServingThePregzippedFile(t, true)
}
func TestServingThePregzippedFileWithoutEncoding(t *testing.T) {
testServingThePregzippedFile(t, false)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment