1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
20 testFile = "testdata/file"
24 var ServeFileRangeTests = []struct {
29 {0, testFileLength, "", StatusOK},
30 {0, 5, "0-4", StatusPartialContent},
31 {2, testFileLength, "2-", StatusPartialContent},
32 {testFileLength - 5, testFileLength, "-5", StatusPartialContent},
33 {3, 8, "3-7", StatusPartialContent},
34 {0, 0, "20-", StatusRequestedRangeNotSatisfiable},
37 func TestServeFile(t *testing.T) {
38 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
39 ServeFile(w, r, "testdata/file")
45 file, err := ioutil.ReadFile(testFile)
47 t.Fatal("reading file:", err)
50 // set up the Request (re-used for all tests)
52 req.Header = make(Header)
53 if req.URL, err = url.Parse(ts.URL); err != nil {
54 t.Fatal("ParseURL:", err)
59 _, body := getBody(t, req)
60 if !equal(body, file) {
61 t.Fatalf("body mismatch: got %q, want %q", body, file)
65 for _, rt := range ServeFileRangeTests {
66 req.Header.Set("Range", "bytes="+rt.r)
68 req.Header["Range"] = nil
70 r, body := getBody(t, req)
71 if r.StatusCode != rt.code {
72 t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
74 if rt.code == StatusRequestedRangeNotSatisfiable {
77 h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
81 cr := r.Header.Get("Content-Range")
83 t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
85 if !equal(body, file[rt.start:rt.end]) {
86 t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
91 var fsRedirectTestData = []struct {
92 original, redirect string
94 {"/test/index.html", "/test/"},
95 {"/test/testdata", "/test/testdata/"},
96 {"/test/testdata/file/", "/test/testdata/file"},
99 func TestFSRedirect(t *testing.T) {
100 ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
103 for _, data := range fsRedirectTestData {
104 res, err := Get(ts.URL + data.original)
109 if g, e := res.Request.URL.Path, data.redirect; g != e {
110 t.Errorf("redirect from %s: got %s, want %s", data.original, g, e)
115 type testFileSystem struct {
116 open func(name string) (File, error)
119 func (fs *testFileSystem) Open(name string) (File, error) {
123 func TestFileServerCleans(t *testing.T) {
124 ch := make(chan string, 1)
125 fs := FileServer(&testFileSystem{func(name string) (File, error) {
127 return nil, os.ENOENT
130 reqPath, openArg string
132 {"/foo.txt", "/foo.txt"},
133 {"//foo.txt", "/foo.txt"},
134 {"/../foo.txt", "/foo.txt"},
136 req, _ := NewRequest("GET", "http://example.com", nil)
137 for n, test := range tests {
138 rec := httptest.NewRecorder()
139 req.URL.Path = test.reqPath
140 fs.ServeHTTP(rec, req)
141 if got := <-ch; got != test.openArg {
142 t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
147 func TestFileServerImplicitLeadingSlash(t *testing.T) {
148 tempDir, err := ioutil.TempDir("", "")
150 t.Fatalf("TempDir: %v", err)
152 defer os.RemoveAll(tempDir)
153 if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil {
154 t.Fatalf("WriteFile: %v", err)
156 ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir))))
158 get := func(suffix string) string {
159 res, err := Get(ts.URL + suffix)
161 t.Fatalf("Get %s: %v", suffix, err)
163 b, err := ioutil.ReadAll(res.Body)
165 t.Fatalf("ReadAll %s: %v", suffix, err)
169 if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
170 t.Logf("expected a directory listing with foo.txt, got %q", s)
172 if s := get("/bar/foo.txt"); s != "Hello world" {
173 t.Logf("expected %q, got %q", "Hello world", s)
177 func TestDirJoin(t *testing.T) {
178 wfi, err := os.Stat("/etc/hosts")
180 t.Logf("skipping test; no /etc/hosts file")
183 test := func(d Dir, name string) {
184 f, err := d.Open(name)
186 t.Fatalf("open of %s: %v", name, err)
191 t.Fatalf("stat of %s: %v", name, err)
193 if gfi.Ino != wfi.Ino {
194 t.Errorf("%s got different inode", name)
197 test(Dir("/etc/"), "/hosts")
198 test(Dir("/etc/"), "hosts")
199 test(Dir("/etc/"), "../../../../hosts")
200 test(Dir("/etc"), "/hosts")
201 test(Dir("/etc"), "hosts")
202 test(Dir("/etc"), "../../../../hosts")
204 // Not really directories, but since we use this trick in
205 // ServeFile, test it:
206 test(Dir("/etc/hosts"), "")
207 test(Dir("/etc/hosts"), "/")
208 test(Dir("/etc/hosts"), "../")
211 func TestEmptyDirOpenCWD(t *testing.T) {
212 test := func(d Dir) {
214 f, err := d.Open(name)
216 t.Fatalf("open of %s: %v", name, err)
225 func TestServeFileContentType(t *testing.T) {
226 const ctype = "icecream/chocolate"
228 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
230 w.Header().Set("Content-Type", ctype)
232 ServeFile(w, r, "testdata/file")
235 get := func(want string) {
236 resp, err := Get(ts.URL)
240 if h := resp.Header.Get("Content-Type"); h != want {
241 t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
244 get("text/plain; charset=utf-8")
249 func TestServeFileMimeType(t *testing.T) {
250 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
251 ServeFile(w, r, "testdata/style.css")
254 resp, err := Get(ts.URL)
258 want := "text/css; charset=utf-8"
259 if h := resp.Header.Get("Content-Type"); h != want {
260 t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
264 func TestServeFileFromCWD(t *testing.T) {
265 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
266 ServeFile(w, r, "fs_test.go")
269 r, err := Get(ts.URL)
273 if r.StatusCode != 200 {
274 t.Fatalf("expected 200 OK, got %s", r.Status)
278 func TestServeFileWithContentEncoding(t *testing.T) {
279 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
280 w.Header().Set("Content-Encoding", "foo")
281 ServeFile(w, r, "testdata/file")
284 resp, err := Get(ts.URL)
288 if g, e := resp.ContentLength, int64(-1); g != e {
289 t.Errorf("Content-Length mismatch: got %d, want %d", g, e)
293 func TestServeIndexHtml(t *testing.T) {
294 const want = "index.html says hello\n"
295 ts := httptest.NewServer(FileServer(Dir(".")))
298 for _, path := range []string{"/testdata/", "/testdata/index.html"} {
299 res, err := Get(ts.URL + path)
303 defer res.Body.Close()
304 b, err := ioutil.ReadAll(res.Body)
306 t.Fatal("reading Body:", err)
308 if s := string(b); s != want {
309 t.Errorf("for path %q got %q, want %q", path, s, want)
314 func getBody(t *testing.T, req Request) (*Response, []byte) {
315 r, err := DefaultClient.Do(&req)
317 t.Fatal(req.URL.String(), "send:", err)
319 b, err := ioutil.ReadAll(r.Body)
321 t.Fatal("reading Body:", err)
326 func equal(a, b []byte) bool {
327 if len(a) != len(b) {