blob: 976ee75c7dd0710a1b6a99afcc5ab1935d14df57 [file] [log] [blame]
Andrew Gerrandaa9c2132010-12-10 08:51:13 +11001// 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.
4
Brad Fitzpatrickf88abda2011-03-05 13:51:35 -08005package http_test
Andrew Gerrandaa9c2132010-12-10 08:51:13 +11006
7import (
8 "fmt"
9 "io/ioutil"
Rob Pike45e3bcb2011-11-08 15:41:54 -080010 . "net/http"
11 "net/http/httptest"
12 "net/url"
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110013 "os"
Brad Fitzpatrick37457162011-07-18 09:04:48 -070014 "path/filepath"
15 "strings"
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110016 "testing"
17)
18
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110019const (
20 testFile = "testdata/file"
21 testFileLength = 11
22)
23
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110024var ServeFileRangeTests = []struct {
25 start, end int
26 r string
27 code int
28}{
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},
35}
36
37func TestServeFile(t *testing.T) {
Brad Fitzpatrickf88abda2011-03-05 13:51:35 -080038 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
39 ServeFile(w, r, "testdata/file")
40 }))
41 defer ts.Close()
42
Russ Coxc2049d22011-11-01 22:04:37 -040043 var err error
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110044
45 file, err := ioutil.ReadFile(testFile)
46 if err != nil {
47 t.Fatal("reading file:", err)
48 }
49
50 // set up the Request (re-used for all tests)
51 var req Request
Petar Maymounkovb8fa6182011-02-23 00:39:25 -050052 req.Header = make(Header)
Rob Pike1d8f8222011-08-17 13:36:02 +100053 if req.URL, err = url.Parse(ts.URL); err != nil {
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110054 t.Fatal("ParseURL:", err)
55 }
56 req.Method = "GET"
57
58 // straight GET
59 _, body := getBody(t, req)
60 if !equal(body, file) {
61 t.Fatalf("body mismatch: got %q, want %q", body, file)
62 }
63
64 // Range tests
65 for _, rt := range ServeFileRangeTests {
Petar Maymounkovb8fa6182011-02-23 00:39:25 -050066 req.Header.Set("Range", "bytes="+rt.r)
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110067 if rt.r == "" {
Petar Maymounkovb8fa6182011-02-23 00:39:25 -050068 req.Header["Range"] = nil
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110069 }
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)
73 }
74 if rt.code == StatusRequestedRangeNotSatisfiable {
75 continue
76 }
Clement Skau49741f22011-01-19 10:05:48 -050077 h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110078 if rt.r == "" {
79 h = ""
80 }
Petar Maymounkovb8fa6182011-02-23 00:39:25 -050081 cr := r.Header.Get("Content-Range")
82 if cr != h {
83 t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
Andrew Gerrandaa9c2132010-12-10 08:51:13 +110084 }
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])
87 }
88 }
89}
90
Andrew Balholm3de62282011-07-28 11:43:16 -070091var fsRedirectTestData = []struct {
92 original, redirect string
93}{
94 {"/test/index.html", "/test/"},
95 {"/test/testdata", "/test/testdata/"},
96 {"/test/testdata/file/", "/test/testdata/file"},
97}
98
99func TestFSRedirect(t *testing.T) {
100 ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
101 defer ts.Close()
102
103 for _, data := range fsRedirectTestData {
104 res, err := Get(ts.URL + data.original)
105 if err != nil {
106 t.Fatal(err)
107 }
108 res.Body.Close()
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)
111 }
112 }
113}
114
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700115type testFileSystem struct {
Russ Coxc2049d22011-11-01 22:04:37 -0400116 open func(name string) (File, error)
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700117}
118
Russ Coxc2049d22011-11-01 22:04:37 -0400119func (fs *testFileSystem) Open(name string) (File, error) {
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700120 return fs.open(name)
121}
122
123func TestFileServerCleans(t *testing.T) {
124 ch := make(chan string, 1)
Russ Coxc2049d22011-11-01 22:04:37 -0400125 fs := FileServer(&testFileSystem{func(name string) (File, error) {
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700126 ch <- name
127 return nil, os.ENOENT
128 }})
129 tests := []struct {
130 reqPath, openArg string
131 }{
132 {"/foo.txt", "/foo.txt"},
133 {"//foo.txt", "/foo.txt"},
134 {"/../foo.txt", "/foo.txt"},
135 }
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)
143 }
144 }
145}
146
Brad Fitzpatrick37457162011-07-18 09:04:48 -0700147func TestFileServerImplicitLeadingSlash(t *testing.T) {
148 tempDir, err := ioutil.TempDir("", "")
149 if err != nil {
150 t.Fatalf("TempDir: %v", err)
151 }
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)
155 }
156 ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir))))
157 defer ts.Close()
158 get := func(suffix string) string {
159 res, err := Get(ts.URL + suffix)
160 if err != nil {
161 t.Fatalf("Get %s: %v", suffix, err)
162 }
163 b, err := ioutil.ReadAll(res.Body)
164 if err != nil {
165 t.Fatalf("ReadAll %s: %v", suffix, err)
166 }
167 return string(b)
168 }
169 if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
170 t.Logf("expected a directory listing with foo.txt, got %q", s)
171 }
172 if s := get("/bar/foo.txt"); s != "Hello world" {
173 t.Logf("expected %q, got %q", "Hello world", s)
174 }
175}
176
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700177func TestDirJoin(t *testing.T) {
178 wfi, err := os.Stat("/etc/hosts")
179 if err != nil {
180 t.Logf("skipping test; no /etc/hosts file")
181 return
182 }
183 test := func(d Dir, name string) {
184 f, err := d.Open(name)
185 if err != nil {
186 t.Fatalf("open of %s: %v", name, err)
187 }
188 defer f.Close()
189 gfi, err := f.Stat()
190 if err != nil {
Rob Pike54045e62011-07-22 17:11:44 +1000191 t.Fatalf("stat of %s: %v", name, err)
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700192 }
Russ Cox8dce57e2011-11-30 12:04:16 -0500193 if !gfi.(*os.FileStat).SameFile(wfi.(*os.FileStat)) {
194 t.Errorf("%s got different file", name)
Brad Fitzpatrick19f79502011-06-27 15:26:36 -0700195 }
196 }
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")
203
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"), "../")
209}
210
Andrew Gerrand0b1bcf82011-11-17 11:42:25 +1100211func TestEmptyDirOpenCWD(t *testing.T) {
212 test := func(d Dir) {
213 name := "fs_test.go"
214 f, err := d.Open(name)
215 if err != nil {
216 t.Fatalf("open of %s: %v", name, err)
217 }
218 defer f.Close()
219 }
220 test(Dir(""))
221 test(Dir("."))
222 test(Dir("./"))
223}
224
Andrew Gerrandb44dbff2011-04-06 14:52:42 +1000225func TestServeFileContentType(t *testing.T) {
226 const ctype = "icecream/chocolate"
227 override := false
228 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
229 if override {
230 w.Header().Set("Content-Type", ctype)
231 }
232 ServeFile(w, r, "testdata/file")
233 }))
234 defer ts.Close()
235 get := func(want string) {
Brad Fitzpatrick05a1b7e2011-05-13 07:31:24 -0700236 resp, err := Get(ts.URL)
Andrew Gerrandb44dbff2011-04-06 14:52:42 +1000237 if err != nil {
238 t.Fatal(err)
239 }
240 if h := resp.Header.Get("Content-Type"); h != want {
Alex Brainman8800f7c2011-08-01 11:50:50 +1000241 t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
Andrew Gerrandb44dbff2011-04-06 14:52:42 +1000242 }
243 }
Brad Fitzpatrick81cfb4e2011-04-22 09:09:37 -0700244 get("text/plain; charset=utf-8")
Andrew Gerrandb44dbff2011-04-06 14:52:42 +1000245 override = true
246 get(ctype)
247}
248
Yasuhiro Matsumoto20122902011-07-13 14:39:33 -0700249func TestServeFileMimeType(t *testing.T) {
250 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
251 ServeFile(w, r, "testdata/style.css")
252 }))
253 defer ts.Close()
254 resp, err := Get(ts.URL)
255 if err != nil {
256 t.Fatal(err)
257 }
Brad Fitzpatrick8ba48fb2011-07-13 15:48:57 -0700258 want := "text/css; charset=utf-8"
Yasuhiro Matsumoto20122902011-07-13 14:39:33 -0700259 if h := resp.Header.Get("Content-Type"); h != want {
260 t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
261 }
262}
263
Andrew Gerrand0b1bcf82011-11-17 11:42:25 +1100264func TestServeFileFromCWD(t *testing.T) {
265 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
266 ServeFile(w, r, "fs_test.go")
267 }))
268 defer ts.Close()
269 r, err := Get(ts.URL)
270 if err != nil {
271 t.Fatal(err)
272 }
273 if r.StatusCode != 200 {
274 t.Fatalf("expected 200 OK, got %s", r.Status)
275 }
276}
277
Brad Fitzpatrick31c79c42011-06-02 13:36:52 -0700278func 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")
282 }))
283 defer ts.Close()
284 resp, err := Get(ts.URL)
285 if err != nil {
286 t.Fatal(err)
287 }
288 if g, e := resp.ContentLength, int64(-1); g != e {
Alex Brainman8800f7c2011-08-01 11:50:50 +1000289 t.Errorf("Content-Length mismatch: got %d, want %d", g, e)
Brad Fitzpatrick31c79c42011-06-02 13:36:52 -0700290 }
291}
292
Yasuhiro Matsumoto29df7bb2011-08-09 10:25:53 -0700293func TestServeIndexHtml(t *testing.T) {
294 const want = "index.html says hello\n"
295 ts := httptest.NewServer(FileServer(Dir(".")))
296 defer ts.Close()
297
298 for _, path := range []string{"/testdata/", "/testdata/index.html"} {
299 res, err := Get(ts.URL + path)
300 if err != nil {
301 t.Fatal(err)
302 }
303 defer res.Body.Close()
304 b, err := ioutil.ReadAll(res.Body)
305 if err != nil {
306 t.Fatal("reading Body:", err)
307 }
308 if s := string(b); s != want {
309 t.Errorf("for path %q got %q, want %q", path, s, want)
310 }
311 }
312}
313
Andrew Gerrandaa9c2132010-12-10 08:51:13 +1100314func getBody(t *testing.T, req Request) (*Response, []byte) {
Brad Fitzpatrickf88abda2011-03-05 13:51:35 -0800315 r, err := DefaultClient.Do(&req)
Andrew Gerrandaa9c2132010-12-10 08:51:13 +1100316 if err != nil {
317 t.Fatal(req.URL.String(), "send:", err)
318 }
319 b, err := ioutil.ReadAll(r.Body)
320 if err != nil {
321 t.Fatal("reading Body:", err)
322 }
323 return r, b
324}
325
326func equal(a, b []byte) bool {
327 if len(a) != len(b) {
328 return false
329 }
330 for i := range a {
331 if a[i] != b[i] {
332 return false
333 }
334 }
335 return true
336}