From 96bfb9f347972568f2eeaa85037fe2f5d62cad41 Mon Sep 17 00:00:00 2001 From: Martin Bertschler Date: Sun, 12 Mar 2017 23:41:49 +0100 Subject: [PATCH] staticfiles: add Content-Length header (closes #1479) (#1492) * staticfiles: add Content-Length header (closes #1479) * make linter happy, rename "Html" in identifiers to "HTML" --- caddyhttp/gzip/gzip.go | 1 + caddyhttp/staticfiles/fileserver.go | 1 + caddyhttp/staticfiles/fileserver_test.go | 138 +++++++++++++---------- 3 files changed, 78 insertions(+), 62 deletions(-) diff --git a/caddyhttp/gzip/gzip.go b/caddyhttp/gzip/gzip.go index d2a790a6a..2389fd99e 100644 --- a/caddyhttp/gzip/gzip.go +++ b/caddyhttp/gzip/gzip.go @@ -12,6 +12,7 @@ import ( "strings" "errors" + "github.com/mholt/caddy" "github.com/mholt/caddy/caddyhttp/httpserver" ) diff --git a/caddyhttp/staticfiles/fileserver.go b/caddyhttp/staticfiles/fileserver.go index c96cc37a4..6db0d8ea3 100644 --- a/caddyhttp/staticfiles/fileserver.go +++ b/caddyhttp/staticfiles/fileserver.go @@ -201,6 +201,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request, name stri w.Header().Add("Vary", "Accept-Encoding") w.Header().Set("Content-Encoding", encoding) + w.Header().Set("Content-Length", strconv.FormatInt(encodedFileInfo.Size(), 10)) defer f.Close() break diff --git a/caddyhttp/staticfiles/fileserver_test.go b/caddyhttp/staticfiles/fileserver_test.go index 058ad7cf6..103d9265b 100644 --- a/caddyhttp/staticfiles/fileserver_test.go +++ b/caddyhttp/staticfiles/fileserver_test.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "testing" "time" @@ -23,16 +24,16 @@ var ( ) var ( - webrootFile1Html = filepath.Join("webroot", "file1.html") - webrootDirFile2Html = filepath.Join("webroot", "dir", "file2.html") - webrootDirHiddenHtml = filepath.Join("webroot", "dir", "hidden.html") - webrootDirwithindexIndeHtml = filepath.Join("webroot", "dirwithindex", "index.html") - webrootSubGzippedHtml = filepath.Join("webroot", "sub", "gzipped.html") - webrootSubGzippedHtmlGz = filepath.Join("webroot", "sub", "gzipped.html.gz") - webrootSubGzippedHtmlBr = filepath.Join("webroot", "sub", "gzipped.html.br") - webrootSubBrotliHtml = filepath.Join("webroot", "sub", "brotli.html") - webrootSubBrotliHtmlGz = filepath.Join("webroot", "sub", "brotli.html.gz") - webrootSubBrotliHtmlBr = filepath.Join("webroot", "sub", "brotli.html.br") + webrootFile1HTML = filepath.Join("webroot", "file1.html") + webrootDirFile2HTML = filepath.Join("webroot", "dir", "file2.html") + webrootDirHiddenHTML = filepath.Join("webroot", "dir", "hidden.html") + webrootDirwithindexIndeHTML = filepath.Join("webroot", "dirwithindex", "index.html") + webrootSubGzippedHTML = filepath.Join("webroot", "sub", "gzipped.html") + webrootSubGzippedHTMLGz = filepath.Join("webroot", "sub", "gzipped.html.gz") + webrootSubGzippedHTMLBr = filepath.Join("webroot", "sub", "gzipped.html.br") + webrootSubBrotliHTML = filepath.Join("webroot", "sub", "brotli.html") + webrootSubBrotliHTMLGz = filepath.Join("webroot", "sub", "brotli.html.gz") + webrootSubBrotliHTMLBr = filepath.Join("webroot", "sub", "brotli.html.br") webrootSubBarDirWithIndexIndexHTML = filepath.Join("webroot", "bar", "dirwithindex", "index.html") ) @@ -49,16 +50,16 @@ var ( // '------ hidden.html var testFiles = map[string]string{ "unreachable.html": "

must not leak

", - webrootFile1Html: "

file1.html

", - webrootDirFile2Html: "

dir/file2.html

", - webrootDirwithindexIndeHtml: "

dirwithindex/index.html

", - webrootDirHiddenHtml: "

dir/hidden.html

", - webrootSubGzippedHtml: "

gzipped.html

", - webrootSubGzippedHtmlGz: "1.gzipped.html.gz", - webrootSubGzippedHtmlBr: "2.gzipped.html.br", - webrootSubBrotliHtml: "3.brotli.html", - webrootSubBrotliHtmlGz: "4.brotli.html.gz", - webrootSubBrotliHtmlBr: "5.brotli.html.br", + webrootFile1HTML: "

file1.html

", + webrootDirFile2HTML: "

dir/file2.html

", + webrootDirwithindexIndeHTML: "

dirwithindex/index.html

", + webrootDirHiddenHTML: "

dir/hidden.html

", + webrootSubGzippedHTML: "

gzipped.html

", + webrootSubGzippedHTMLGz: "1.gzipped.html.gz", + webrootSubGzippedHTMLBr: "2.gzipped.html.br", + webrootSubBrotliHTML: "3.brotli.html", + webrootSubBrotliHTMLGz: "4.brotli.html.gz", + webrootSubBrotliHTMLBr: "5.brotli.html.br", webrootSubBarDirWithIndexIndexHTML: "

bar/dirwithindex/index.html

", } @@ -76,15 +77,16 @@ func TestServeHTTP(t *testing.T) { movedPermanently := "Moved Permanently" tests := []struct { - url string - cleanedPath string - acceptEncoding string - expectedLocation string - expectedStatus int - expectedBodyContent string - expectedEtag string - expectedVary string - expectedEncoding string + url string + cleanedPath string + acceptEncoding string + expectedLocation string + expectedStatus int + expectedBodyContent string + expectedEtag string + expectedVary string + expectedEncoding string + expectedContentLength string }{ // Test 0 - access without any path { @@ -98,17 +100,19 @@ func TestServeHTTP(t *testing.T) { }, // Test 2 - access existing file { - url: "https://foo/file1.html", - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootFile1Html], - expectedEtag: `"2n9cj"`, + url: "https://foo/file1.html", + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootFile1HTML], + expectedEtag: `"2n9cj"`, + expectedContentLength: strconv.Itoa(len(testFiles[webrootFile1HTML])), }, // Test 3 - access folder with index file with trailing slash { - url: "https://foo/dirwithindex/", - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootDirwithindexIndeHtml], - expectedEtag: `"2n9cw"`, + url: "https://foo/dirwithindex/", + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootDirwithindexIndeHTML], + expectedEtag: `"2n9cw"`, + expectedContentLength: strconv.Itoa(len(testFiles[webrootDirwithindexIndeHTML])), }, // Test 4 - access folder with index file without trailing slash { @@ -148,10 +152,11 @@ func TestServeHTTP(t *testing.T) { }, // Test 10 - access a index file directly { - url: "https://foo/dirwithindex/index.html", - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootDirwithindexIndeHtml], - expectedEtag: `"2n9cw"`, + url: "https://foo/dirwithindex/index.html", + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootDirwithindexIndeHTML], + expectedEtag: `"2n9cw"`, + expectedContentLength: strconv.Itoa(len(testFiles[webrootDirwithindexIndeHTML])), }, // Test 11 - send a request with query params { @@ -193,33 +198,36 @@ func TestServeHTTP(t *testing.T) { }, // Test 18 - try to get pre-gzipped file. { - url: "https://foo/sub/gzipped.html", - acceptEncoding: "gzip", - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootSubGzippedHtmlGz], - expectedEtag: `"2n9ch"`, - expectedVary: "Accept-Encoding", - expectedEncoding: "gzip", + url: "https://foo/sub/gzipped.html", + acceptEncoding: "gzip", + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootSubGzippedHTMLGz], + expectedEtag: `"2n9ch"`, + expectedVary: "Accept-Encoding", + expectedEncoding: "gzip", + expectedContentLength: strconv.Itoa(len(testFiles[webrootSubGzippedHTMLGz])), }, // Test 19 - try to get pre-brotli encoded file. { - url: "https://foo/sub/brotli.html", - acceptEncoding: "br,gzip", - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootSubBrotliHtmlBr], - expectedEtag: `"2n9cg"`, - expectedVary: "Accept-Encoding", - expectedEncoding: "br", + url: "https://foo/sub/brotli.html", + acceptEncoding: "br,gzip", + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootSubBrotliHTMLBr], + expectedEtag: `"2n9cg"`, + expectedVary: "Accept-Encoding", + expectedEncoding: "br", + expectedContentLength: strconv.Itoa(len(testFiles[webrootSubBrotliHTMLBr])), }, // Test 20 - not allowed to get pre-brotli encoded file. { - url: "https://foo/sub/brotli.html", - acceptEncoding: "nicebrew", // contains "br" substring but not "br" - expectedStatus: http.StatusOK, - expectedBodyContent: testFiles[webrootSubBrotliHtml], - expectedEtag: `"2n9cd"`, - expectedVary: "", - expectedEncoding: "", + url: "https://foo/sub/brotli.html", + acceptEncoding: "nicebrew", // contains "br" substring but not "br" + expectedStatus: http.StatusOK, + expectedBodyContent: testFiles[webrootSubBrotliHTML], + expectedEtag: `"2n9cd"`, + expectedVary: "", + expectedEncoding: "", + expectedContentLength: strconv.Itoa(len(testFiles[webrootSubBrotliHTML])), }, // Test 20 - treat existing file as a directory. { @@ -280,6 +288,7 @@ func TestServeHTTP(t *testing.T) { body := responseRecorder.Body.String() vary := responseRecorder.Header().Get("Vary") encoding := responseRecorder.Header().Get("Content-Encoding") + length := responseRecorder.Header().Get("Content-Length") // check if error matches expectations if err != nil { @@ -317,6 +326,11 @@ func TestServeHTTP(t *testing.T) { t.Errorf("Test %d: Expected Location header %q, found %q", i, test.expectedLocation, l) } } + + // check content length + if test.expectedContentLength != length { + t.Errorf("Test %d: Expected Content-Length header %s, found %s", i, test.expectedContentLength, length) + } } }