// Copyright 2013 The Go Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd. package httputil_test import ( "crypto/sha1" "encoding/hex" "io/ioutil" "net/http" "net/http/httptest" "net/url" "os" "strconv" "testing" "time" "github.com/golang/gddo/httputil" "github.com/google/go-cmp/cmp" ) var ( testHash = computeTestHash() testEtag = `"` + testHash + `"` testContentLength = computeTestContentLength() ) func mustParseURL(urlStr string) *url.URL { u, err := url.Parse(urlStr) if err != nil { panic(err) } return u } func computeTestHash() string { p, err := ioutil.ReadFile("static_test.go") if err != nil { panic(err) } w := sha1.New() w.Write(p) return hex.EncodeToString(w.Sum(nil)) } func computeTestContentLength() string { info, err := os.Stat("static_test.go") if err != nil { panic(err) } return strconv.FormatInt(info.Size(), 10) } var fileServerTests = []*struct { name string // test name for log ss *httputil.StaticServer r *http.Request header http.Header // expected response headers status int // expected response status empty bool // true if response body not expected. }{ { name: "get", ss: &httputil.StaticServer{MaxAge: 3 * time.Second}, r: &http.Request{ URL: mustParseURL("/dir/static_test.go"), Method: "GET", }, status: http.StatusOK, header: http.Header{ "Etag": {testEtag}, "Cache-Control": {"public, max-age=3"}, "Content-Length": {testContentLength}, "Content-Type": {"application/octet-stream"}, }, }, { name: "get .", ss: &httputil.StaticServer{Dir: ".", MaxAge: 3 * time.Second}, r: &http.Request{ URL: mustParseURL("/dir/static_test.go"), Method: "GET", }, status: http.StatusOK, header: http.Header{ "Etag": {testEtag}, "Cache-Control": {"public, max-age=3"}, "Content-Length": {testContentLength}, "Content-Type": {"application/octet-stream"}, }, }, { name: "get with ?v=", ss: &httputil.StaticServer{MaxAge: 3 * time.Second}, r: &http.Request{ URL: mustParseURL("/dir/static_test.go?v=xxxxx"), Method: "GET", }, status: http.StatusOK, header: http.Header{ "Etag": {testEtag}, "Cache-Control": {"public, max-age=31536000"}, "Content-Length": {testContentLength}, "Content-Type": {"application/octet-stream"}, }, }, { name: "head", ss: &httputil.StaticServer{MaxAge: 3 * time.Second}, r: &http.Request{ URL: mustParseURL("/dir/static_test.go"), Method: "HEAD", }, status: http.StatusOK, header: http.Header{ "Etag": {testEtag}, "Cache-Control": {"public, max-age=3"}, "Content-Length": {testContentLength}, "Content-Type": {"application/octet-stream"}, }, empty: true, }, { name: "if-none-match", ss: &httputil.StaticServer{MaxAge: 3 * time.Second}, r: &http.Request{ URL: mustParseURL("/dir/static_test.go"), Method: "GET", Header: http.Header{"If-None-Match": {testEtag}}, }, status: http.StatusNotModified, header: http.Header{ "Cache-Control": {"public, max-age=3"}, "Etag": {testEtag}, }, empty: true, }, } func testStaticServer(t *testing.T, f func(*httputil.StaticServer) http.Handler) { for _, tt := range fileServerTests { w := httptest.NewRecorder() h := f(tt.ss) h.ServeHTTP(w, tt.r) if w.Code != tt.status { t.Errorf("%s, status=%d, want %d", tt.name, w.Code, tt.status) } if !cmp.Equal(w.HeaderMap, tt.header) { t.Errorf("%s\n\theader=%v,\n\twant %v", tt.name, w.HeaderMap, tt.header) } empty := w.Body.Len() == 0 if empty != tt.empty { t.Errorf("%s empty=%v, want %v", tt.name, empty, tt.empty) } } } func TestFileHandler(t *testing.T) { testStaticServer(t, func(ss *httputil.StaticServer) http.Handler { return ss.FileHandler("static_test.go") }) } func TestDirectoryHandler(t *testing.T) { testStaticServer(t, func(ss *httputil.StaticServer) http.Handler { return ss.DirectoryHandler("/dir", ".") }) } func TestFilesHandler(t *testing.T) { testStaticServer(t, func(ss *httputil.StaticServer) http.Handler { return ss.FilesHandler("static_test.go") }) }