~ubuntu-branches/ubuntu/vivid/golang/vivid

« back to all changes in this revision

Viewing changes to src/pkg/net/http/fs_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
        "fmt"
11
11
        "io"
12
12
        "io/ioutil"
 
13
        "mime"
 
14
        "mime/multipart"
13
15
        "net"
14
16
        . "net/http"
15
17
        "net/http/httptest"
16
18
        "net/url"
17
19
        "os"
18
20
        "os/exec"
 
21
        "path"
19
22
        "path/filepath"
20
23
        "regexp"
21
24
        "runtime"
25
28
)
26
29
 
27
30
const (
28
 
        testFile       = "testdata/file"
29
 
        testFileLength = 11
 
31
        testFile    = "testdata/file"
 
32
        testFileLen = 11
30
33
)
31
34
 
 
35
type wantRange struct {
 
36
        start, end int64 // range [start,end)
 
37
}
 
38
 
32
39
var ServeFileRangeTests = []struct {
33
 
        start, end int
34
 
        r          string
35
 
        code       int
 
40
        r      string
 
41
        code   int
 
42
        ranges []wantRange
36
43
}{
37
 
        {0, testFileLength, "", StatusOK},
38
 
        {0, 5, "0-4", StatusPartialContent},
39
 
        {2, testFileLength, "2-", StatusPartialContent},
40
 
        {testFileLength - 5, testFileLength, "-5", StatusPartialContent},
41
 
        {3, 8, "3-7", StatusPartialContent},
42
 
        {0, 0, "20-", StatusRequestedRangeNotSatisfiable},
 
44
        {r: "", code: StatusOK},
 
45
        {r: "bytes=0-4", code: StatusPartialContent, ranges: []wantRange{{0, 5}}},
 
46
        {r: "bytes=2-", code: StatusPartialContent, ranges: []wantRange{{2, testFileLen}}},
 
47
        {r: "bytes=-5", code: StatusPartialContent, ranges: []wantRange{{testFileLen - 5, testFileLen}}},
 
48
        {r: "bytes=3-7", code: StatusPartialContent, ranges: []wantRange{{3, 8}}},
 
49
        {r: "bytes=20-", code: StatusRequestedRangeNotSatisfiable},
 
50
        {r: "bytes=0-0,-2", code: StatusPartialContent, ranges: []wantRange{{0, 1}, {testFileLen - 2, testFileLen}}},
 
51
        {r: "bytes=0-1,5-8", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, 9}}},
 
52
        {r: "bytes=0-1,5-", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, testFileLen}}},
 
53
        {r: "bytes=0-,1-,2-,3-,4-", code: StatusOK}, // ignore wasteful range request
43
54
}
44
55
 
45
56
func TestServeFile(t *testing.T) {
 
57
        defer afterTest(t)
46
58
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
47
59
                ServeFile(w, r, "testdata/file")
48
60
        }))
65
77
 
66
78
        // straight GET
67
79
        _, body := getBody(t, "straight get", req)
68
 
        if !equal(body, file) {
 
80
        if !bytes.Equal(body, file) {
69
81
                t.Fatalf("body mismatch: got %q, want %q", body, file)
70
82
        }
71
83
 
72
84
        // Range tests
73
 
        for i, rt := range ServeFileRangeTests {
74
 
                req.Header.Set("Range", "bytes="+rt.r)
75
 
                if rt.r == "" {
76
 
                        req.Header["Range"] = nil
 
85
Cases:
 
86
        for _, rt := range ServeFileRangeTests {
 
87
                if rt.r != "" {
 
88
                        req.Header.Set("Range", rt.r)
77
89
                }
78
 
                r, body := getBody(t, fmt.Sprintf("test %d", i), req)
79
 
                if r.StatusCode != rt.code {
80
 
                        t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
 
90
                resp, body := getBody(t, fmt.Sprintf("range test %q", rt.r), req)
 
91
                if resp.StatusCode != rt.code {
 
92
                        t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, resp.StatusCode, rt.code)
81
93
                }
82
94
                if rt.code == StatusRequestedRangeNotSatisfiable {
83
95
                        continue
84
96
                }
85
 
                h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
86
 
                if rt.r == "" {
87
 
                        h = ""
88
 
                }
89
 
                cr := r.Header.Get("Content-Range")
90
 
                if cr != h {
91
 
                        t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
92
 
                }
93
 
                if !equal(body, file[rt.start:rt.end]) {
94
 
                        t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
 
97
                wantContentRange := ""
 
98
                if len(rt.ranges) == 1 {
 
99
                        rng := rt.ranges[0]
 
100
                        wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
 
101
                }
 
102
                cr := resp.Header.Get("Content-Range")
 
103
                if cr != wantContentRange {
 
104
                        t.Errorf("range=%q: Content-Range = %q, want %q", rt.r, cr, wantContentRange)
 
105
                }
 
106
                ct := resp.Header.Get("Content-Type")
 
107
                if len(rt.ranges) == 1 {
 
108
                        rng := rt.ranges[0]
 
109
                        wantBody := file[rng.start:rng.end]
 
110
                        if !bytes.Equal(body, wantBody) {
 
111
                                t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
 
112
                        }
 
113
                        if strings.HasPrefix(ct, "multipart/byteranges") {
 
114
                                t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct)
 
115
                        }
 
116
                }
 
117
                if len(rt.ranges) > 1 {
 
118
                        typ, params, err := mime.ParseMediaType(ct)
 
119
                        if err != nil {
 
120
                                t.Errorf("range=%q content-type = %q; %v", rt.r, ct, err)
 
121
                                continue
 
122
                        }
 
123
                        if typ != "multipart/byteranges" {
 
124
                                t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ)
 
125
                                continue
 
126
                        }
 
127
                        if params["boundary"] == "" {
 
128
                                t.Errorf("range=%q content-type = %q; lacks boundary", rt.r, ct)
 
129
                                continue
 
130
                        }
 
131
                        if g, w := resp.ContentLength, int64(len(body)); g != w {
 
132
                                t.Errorf("range=%q Content-Length = %d; want %d", rt.r, g, w)
 
133
                                continue
 
134
                        }
 
135
                        mr := multipart.NewReader(bytes.NewReader(body), params["boundary"])
 
136
                        for ri, rng := range rt.ranges {
 
137
                                part, err := mr.NextPart()
 
138
                                if err != nil {
 
139
                                        t.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err)
 
140
                                        continue Cases
 
141
                                }
 
142
                                wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
 
143
                                if g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {
 
144
                                        t.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
 
145
                                }
 
146
                                body, err := ioutil.ReadAll(part)
 
147
                                if err != nil {
 
148
                                        t.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err)
 
149
                                        continue Cases
 
150
                                }
 
151
                                wantBody := file[rng.start:rng.end]
 
152
                                if !bytes.Equal(body, wantBody) {
 
153
                                        t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
 
154
                                }
 
155
                        }
 
156
                        _, err = mr.NextPart()
 
157
                        if err != io.EOF {
 
158
                                t.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err)
 
159
                        }
95
160
                }
96
161
        }
97
162
}
105
170
}
106
171
 
107
172
func TestFSRedirect(t *testing.T) {
 
173
        defer afterTest(t)
108
174
        ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
109
175
        defer ts.Close()
110
176
 
129
195
}
130
196
 
131
197
func TestFileServerCleans(t *testing.T) {
 
198
        defer afterTest(t)
132
199
        ch := make(chan string, 1)
133
200
        fs := FileServer(&testFileSystem{func(name string) (File, error) {
134
201
                ch <- name
160
227
}
161
228
 
162
229
func TestFileServerImplicitLeadingSlash(t *testing.T) {
 
230
        defer afterTest(t)
163
231
        tempDir, err := ioutil.TempDir("", "")
164
232
        if err != nil {
165
233
                t.Fatalf("TempDir: %v", err)
193
261
func TestDirJoin(t *testing.T) {
194
262
        wfi, err := os.Stat("/etc/hosts")
195
263
        if err != nil {
196
 
                t.Logf("skipping test; no /etc/hosts file")
197
 
                return
 
264
                t.Skip("skipping test; no /etc/hosts file")
198
265
        }
199
266
        test := func(d Dir, name string) {
200
267
                f, err := d.Open(name)
239
306
}
240
307
 
241
308
func TestServeFileContentType(t *testing.T) {
 
309
        defer afterTest(t)
242
310
        const ctype = "icecream/chocolate"
243
311
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
244
312
                if r.FormValue("override") == "1" {
255
323
                if h := resp.Header.Get("Content-Type"); h != want {
256
324
                        t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
257
325
                }
 
326
                resp.Body.Close()
258
327
        }
259
328
        get("0", "text/plain; charset=utf-8")
260
329
        get("1", ctype)
261
330
}
262
331
 
263
332
func TestServeFileMimeType(t *testing.T) {
 
333
        defer afterTest(t)
264
334
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
265
335
                ServeFile(w, r, "testdata/style.css")
266
336
        }))
269
339
        if err != nil {
270
340
                t.Fatal(err)
271
341
        }
 
342
        resp.Body.Close()
272
343
        want := "text/css; charset=utf-8"
273
344
        if h := resp.Header.Get("Content-Type"); h != want {
274
345
                t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
276
347
}
277
348
 
278
349
func TestServeFileFromCWD(t *testing.T) {
 
350
        defer afterTest(t)
279
351
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
280
352
                ServeFile(w, r, "fs_test.go")
281
353
        }))
284
356
        if err != nil {
285
357
                t.Fatal(err)
286
358
        }
 
359
        r.Body.Close()
287
360
        if r.StatusCode != 200 {
288
361
                t.Fatalf("expected 200 OK, got %s", r.Status)
289
362
        }
290
363
}
291
364
 
292
365
func TestServeFileWithContentEncoding(t *testing.T) {
 
366
        defer afterTest(t)
293
367
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
294
368
                w.Header().Set("Content-Encoding", "foo")
295
369
                ServeFile(w, r, "testdata/file")
299
373
        if err != nil {
300
374
                t.Fatal(err)
301
375
        }
 
376
        resp.Body.Close()
302
377
        if g, e := resp.ContentLength, int64(-1); g != e {
303
378
                t.Errorf("Content-Length mismatch: got %d, want %d", g, e)
304
379
        }
305
380
}
306
381
 
307
382
func TestServeIndexHtml(t *testing.T) {
 
383
        defer afterTest(t)
308
384
        const want = "index.html says hello\n"
309
385
        ts := httptest.NewServer(FileServer(Dir(".")))
310
386
        defer ts.Close()
325
401
        }
326
402
}
327
403
 
 
404
func TestFileServerZeroByte(t *testing.T) {
 
405
        defer afterTest(t)
 
406
        ts := httptest.NewServer(FileServer(Dir(".")))
 
407
        defer ts.Close()
 
408
 
 
409
        res, err := Get(ts.URL + "/..\x00")
 
410
        if err != nil {
 
411
                t.Fatal(err)
 
412
        }
 
413
        b, err := ioutil.ReadAll(res.Body)
 
414
        if err != nil {
 
415
                t.Fatal("reading Body:", err)
 
416
        }
 
417
        if res.StatusCode == 200 {
 
418
                t.Errorf("got status 200; want an error. Body is:\n%s", string(b))
 
419
        }
 
420
}
 
421
 
 
422
type fakeFileInfo struct {
 
423
        dir      bool
 
424
        basename string
 
425
        modtime  time.Time
 
426
        ents     []*fakeFileInfo
 
427
        contents string
 
428
}
 
429
 
 
430
func (f *fakeFileInfo) Name() string       { return f.basename }
 
431
func (f *fakeFileInfo) Sys() interface{}   { return nil }
 
432
func (f *fakeFileInfo) ModTime() time.Time { return f.modtime }
 
433
func (f *fakeFileInfo) IsDir() bool        { return f.dir }
 
434
func (f *fakeFileInfo) Size() int64        { return int64(len(f.contents)) }
 
435
func (f *fakeFileInfo) Mode() os.FileMode {
 
436
        if f.dir {
 
437
                return 0755 | os.ModeDir
 
438
        }
 
439
        return 0644
 
440
}
 
441
 
 
442
type fakeFile struct {
 
443
        io.ReadSeeker
 
444
        fi   *fakeFileInfo
 
445
        path string // as opened
 
446
}
 
447
 
 
448
func (f *fakeFile) Close() error               { return nil }
 
449
func (f *fakeFile) Stat() (os.FileInfo, error) { return f.fi, nil }
 
450
func (f *fakeFile) Readdir(count int) ([]os.FileInfo, error) {
 
451
        if !f.fi.dir {
 
452
                return nil, os.ErrInvalid
 
453
        }
 
454
        var fis []os.FileInfo
 
455
        for _, fi := range f.fi.ents {
 
456
                fis = append(fis, fi)
 
457
        }
 
458
        return fis, nil
 
459
}
 
460
 
 
461
type fakeFS map[string]*fakeFileInfo
 
462
 
 
463
func (fs fakeFS) Open(name string) (File, error) {
 
464
        name = path.Clean(name)
 
465
        f, ok := fs[name]
 
466
        if !ok {
 
467
                println("fake filesystem didn't find file", name)
 
468
                return nil, os.ErrNotExist
 
469
        }
 
470
        return &fakeFile{ReadSeeker: strings.NewReader(f.contents), fi: f, path: name}, nil
 
471
}
 
472
 
 
473
func TestDirectoryIfNotModified(t *testing.T) {
 
474
        defer afterTest(t)
 
475
        const indexContents = "I am a fake index.html file"
 
476
        fileMod := time.Unix(1000000000, 0).UTC()
 
477
        fileModStr := fileMod.Format(TimeFormat)
 
478
        dirMod := time.Unix(123, 0).UTC()
 
479
        indexFile := &fakeFileInfo{
 
480
                basename: "index.html",
 
481
                modtime:  fileMod,
 
482
                contents: indexContents,
 
483
        }
 
484
        fs := fakeFS{
 
485
                "/": &fakeFileInfo{
 
486
                        dir:     true,
 
487
                        modtime: dirMod,
 
488
                        ents:    []*fakeFileInfo{indexFile},
 
489
                },
 
490
                "/index.html": indexFile,
 
491
        }
 
492
 
 
493
        ts := httptest.NewServer(FileServer(fs))
 
494
        defer ts.Close()
 
495
 
 
496
        res, err := Get(ts.URL)
 
497
        if err != nil {
 
498
                t.Fatal(err)
 
499
        }
 
500
        b, err := ioutil.ReadAll(res.Body)
 
501
        if err != nil {
 
502
                t.Fatal(err)
 
503
        }
 
504
        if string(b) != indexContents {
 
505
                t.Fatalf("Got body %q; want %q", b, indexContents)
 
506
        }
 
507
        res.Body.Close()
 
508
 
 
509
        lastMod := res.Header.Get("Last-Modified")
 
510
        if lastMod != fileModStr {
 
511
                t.Fatalf("initial Last-Modified = %q; want %q", lastMod, fileModStr)
 
512
        }
 
513
 
 
514
        req, _ := NewRequest("GET", ts.URL, nil)
 
515
        req.Header.Set("If-Modified-Since", lastMod)
 
516
 
 
517
        res, err = DefaultClient.Do(req)
 
518
        if err != nil {
 
519
                t.Fatal(err)
 
520
        }
 
521
        if res.StatusCode != 304 {
 
522
                t.Fatalf("Code after If-Modified-Since request = %v; want 304", res.StatusCode)
 
523
        }
 
524
        res.Body.Close()
 
525
 
 
526
        // Advance the index.html file's modtime, but not the directory's.
 
527
        indexFile.modtime = indexFile.modtime.Add(1 * time.Hour)
 
528
 
 
529
        res, err = DefaultClient.Do(req)
 
530
        if err != nil {
 
531
                t.Fatal(err)
 
532
        }
 
533
        if res.StatusCode != 200 {
 
534
                t.Fatalf("Code after second If-Modified-Since request = %v; want 200; res is %#v", res.StatusCode, res)
 
535
        }
 
536
        res.Body.Close()
 
537
}
 
538
 
 
539
func mustStat(t *testing.T, fileName string) os.FileInfo {
 
540
        fi, err := os.Stat(fileName)
 
541
        if err != nil {
 
542
                t.Fatal(err)
 
543
        }
 
544
        return fi
 
545
}
 
546
 
328
547
func TestServeContent(t *testing.T) {
329
 
        type req struct {
330
 
                name    string
331
 
                modtime time.Time
332
 
                content io.ReadSeeker
 
548
        defer afterTest(t)
 
549
        type serveParam struct {
 
550
                name        string
 
551
                modtime     time.Time
 
552
                content     io.ReadSeeker
 
553
                contentType string
 
554
                etag        string
333
555
        }
334
 
        ch := make(chan req, 1)
 
556
        servec := make(chan serveParam, 1)
335
557
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
336
 
                p := <-ch
 
558
                p := <-servec
 
559
                if p.etag != "" {
 
560
                        w.Header().Set("ETag", p.etag)
 
561
                }
 
562
                if p.contentType != "" {
 
563
                        w.Header().Set("Content-Type", p.contentType)
 
564
                }
337
565
                ServeContent(w, r, p.name, p.modtime, p.content)
338
566
        }))
339
567
        defer ts.Close()
340
568
 
341
 
        css, err := os.Open("testdata/style.css")
342
 
        if err != nil {
343
 
                t.Fatal(err)
344
 
        }
345
 
        defer css.Close()
346
 
 
347
 
        ch <- req{"style.css", time.Time{}, css}
348
 
        res, err := Get(ts.URL)
349
 
        if err != nil {
350
 
                t.Fatal(err)
351
 
        }
352
 
        if g, e := res.Header.Get("Content-Type"), "text/css; charset=utf-8"; g != e {
353
 
                t.Errorf("style.css: content type = %q, want %q", g, e)
354
 
        }
355
 
        if g := res.Header.Get("Last-Modified"); g != "" {
356
 
                t.Errorf("want empty Last-Modified; got %q", g)
357
 
        }
358
 
 
359
 
        fi, err := css.Stat()
360
 
        if err != nil {
361
 
                t.Fatal(err)
362
 
        }
363
 
        ch <- req{"style.html", fi.ModTime(), css}
364
 
        res, err = Get(ts.URL)
365
 
        if err != nil {
366
 
                t.Fatal(err)
367
 
        }
368
 
        if g, e := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != e {
369
 
                t.Errorf("style.html: content type = %q, want %q", g, e)
370
 
        }
371
 
        if g := res.Header.Get("Last-Modified"); g == "" {
372
 
                t.Errorf("want non-empty last-modified")
 
569
        type testCase struct {
 
570
                file             string
 
571
                modtime          time.Time
 
572
                serveETag        string // optional
 
573
                serveContentType string // optional
 
574
                reqHeader        map[string]string
 
575
                wantLastMod      string
 
576
                wantContentType  string
 
577
                wantStatus       int
 
578
        }
 
579
        htmlModTime := mustStat(t, "testdata/index.html").ModTime()
 
580
        tests := map[string]testCase{
 
581
                "no_last_modified": {
 
582
                        file:            "testdata/style.css",
 
583
                        wantContentType: "text/css; charset=utf-8",
 
584
                        wantStatus:      200,
 
585
                },
 
586
                "with_last_modified": {
 
587
                        file:            "testdata/index.html",
 
588
                        wantContentType: "text/html; charset=utf-8",
 
589
                        modtime:         htmlModTime,
 
590
                        wantLastMod:     htmlModTime.UTC().Format(TimeFormat),
 
591
                        wantStatus:      200,
 
592
                },
 
593
                "not_modified_modtime": {
 
594
                        file:    "testdata/style.css",
 
595
                        modtime: htmlModTime,
 
596
                        reqHeader: map[string]string{
 
597
                                "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
 
598
                        },
 
599
                        wantStatus: 304,
 
600
                },
 
601
                "not_modified_modtime_with_contenttype": {
 
602
                        file:             "testdata/style.css",
 
603
                        serveContentType: "text/css", // explicit content type
 
604
                        modtime:          htmlModTime,
 
605
                        reqHeader: map[string]string{
 
606
                                "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
 
607
                        },
 
608
                        wantStatus: 304,
 
609
                },
 
610
                "not_modified_etag": {
 
611
                        file:      "testdata/style.css",
 
612
                        serveETag: `"foo"`,
 
613
                        reqHeader: map[string]string{
 
614
                                "If-None-Match": `"foo"`,
 
615
                        },
 
616
                        wantStatus: 304,
 
617
                },
 
618
                "range_good": {
 
619
                        file:      "testdata/style.css",
 
620
                        serveETag: `"A"`,
 
621
                        reqHeader: map[string]string{
 
622
                                "Range": "bytes=0-4",
 
623
                        },
 
624
                        wantStatus:      StatusPartialContent,
 
625
                        wantContentType: "text/css; charset=utf-8",
 
626
                },
 
627
                // An If-Range resource for entity "A", but entity "B" is now current.
 
628
                // The Range request should be ignored.
 
629
                "range_no_match": {
 
630
                        file:      "testdata/style.css",
 
631
                        serveETag: `"A"`,
 
632
                        reqHeader: map[string]string{
 
633
                                "Range":    "bytes=0-4",
 
634
                                "If-Range": `"B"`,
 
635
                        },
 
636
                        wantStatus:      200,
 
637
                        wantContentType: "text/css; charset=utf-8",
 
638
                },
 
639
        }
 
640
        for testName, tt := range tests {
 
641
                f, err := os.Open(tt.file)
 
642
                if err != nil {
 
643
                        t.Fatalf("test %q: %v", testName, err)
 
644
                }
 
645
                defer f.Close()
 
646
 
 
647
                servec <- serveParam{
 
648
                        name:        filepath.Base(tt.file),
 
649
                        content:     f,
 
650
                        modtime:     tt.modtime,
 
651
                        etag:        tt.serveETag,
 
652
                        contentType: tt.serveContentType,
 
653
                }
 
654
                req, err := NewRequest("GET", ts.URL, nil)
 
655
                if err != nil {
 
656
                        t.Fatal(err)
 
657
                }
 
658
                for k, v := range tt.reqHeader {
 
659
                        req.Header.Set(k, v)
 
660
                }
 
661
                res, err := DefaultClient.Do(req)
 
662
                if err != nil {
 
663
                        t.Fatal(err)
 
664
                }
 
665
                io.Copy(ioutil.Discard, res.Body)
 
666
                res.Body.Close()
 
667
                if res.StatusCode != tt.wantStatus {
 
668
                        t.Errorf("test %q: status = %d; want %d", testName, res.StatusCode, tt.wantStatus)
 
669
                }
 
670
                if g, e := res.Header.Get("Content-Type"), tt.wantContentType; g != e {
 
671
                        t.Errorf("test %q: content-type = %q, want %q", testName, g, e)
 
672
                }
 
673
                if g, e := res.Header.Get("Last-Modified"), tt.wantLastMod; g != e {
 
674
                        t.Errorf("test %q: last-modified = %q, want %q", testName, g, e)
 
675
                }
373
676
        }
374
677
}
375
678
 
376
679
// verifies that sendfile is being used on Linux
377
680
func TestLinuxSendfile(t *testing.T) {
 
681
        defer afterTest(t)
378
682
        if runtime.GOOS != "linux" {
379
 
                t.Logf("skipping; linux-only test")
380
 
                return
 
683
                t.Skip("skipping; linux-only test")
381
684
        }
382
 
        _, err := exec.LookPath("strace")
383
 
        if err != nil {
384
 
                t.Logf("skipping; strace not found in path")
385
 
                return
 
685
        if _, err := exec.LookPath("strace"); err != nil {
 
686
                t.Skip("skipping; strace not found in path")
386
687
        }
387
688
 
388
689
        ln, err := net.Listen("tcp", "127.0.0.1:0")
396
697
        defer ln.Close()
397
698
 
398
699
        var buf bytes.Buffer
399
 
        child := exec.Command("strace", "-f", os.Args[0], "-test.run=TestLinuxSendfileChild")
 
700
        child := exec.Command("strace", "-f", "-q", "-e", "trace=sendfile,sendfile64", os.Args[0], "-test.run=TestLinuxSendfileChild")
400
701
        child.ExtraFiles = append(child.ExtraFiles, lnf)
401
702
        child.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
402
703
        child.Stdout = &buf
403
704
        child.Stderr = &buf
404
 
        err = child.Start()
405
 
        if err != nil {
406
 
                t.Logf("skipping; failed to start straced child: %v", err)
407
 
                return
 
705
        if err := child.Start(); err != nil {
 
706
                t.Skipf("skipping; failed to start straced child: %v", err)
408
707
        }
409
708
 
410
709
        res, err := Get(fmt.Sprintf("http://%s/", ln.Addr()))
464
763
                panic(err)
465
764
        }
466
765
}
467
 
 
468
 
func equal(a, b []byte) bool {
469
 
        if len(a) != len(b) {
470
 
                return false
471
 
        }
472
 
        for i := range a {
473
 
                if a[i] != b[i] {
474
 
                        return false
475
 
                }
476
 
        }
477
 
        return true
478
 
}