LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2010 The Go Authors. All rights reserved. | 1 // Copyright 2010 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // End-to-end serving tests | 5 // End-to-end serving tests |
6 | 6 |
7 package http_test | 7 package http_test |
8 | 8 |
9 import ( | 9 import ( |
10 "bufio" | 10 "bufio" |
11 "bytes" | 11 "bytes" |
12 "crypto/tls" | 12 "crypto/tls" |
| 13 "errors" |
13 "fmt" | 14 "fmt" |
14 "io" | 15 "io" |
15 "io/ioutil" | 16 "io/ioutil" |
16 "log" | 17 "log" |
17 "net" | 18 "net" |
18 . "net/http" | 19 . "net/http" |
19 "net/http/httptest" | 20 "net/http/httptest" |
20 "net/http/httputil" | 21 "net/http/httputil" |
21 "net/url" | 22 "net/url" |
22 "os" | 23 "os" |
(...skipping 1421 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1444 res, err = Get(ts.URL + "/second") | 1445 res, err = Get(ts.URL + "/second") |
1445 if err != nil { | 1446 if err != nil { |
1446 t.Fatal(err) | 1447 t.Fatal(err) |
1447 } | 1448 } |
1448 res.Body.Close() | 1449 res.Body.Close() |
1449 if got := <-uric; got != "/second" { | 1450 if got := <-uric; got != "/second" { |
1450 t.Errorf("Handler saw request for %q; want /second", got) | 1451 t.Errorf("Handler saw request for %q; want /second", got) |
1451 } | 1452 } |
1452 } | 1453 } |
1453 | 1454 |
| 1455 // Tests regarding the ordering of Write, WriteHeader, Header, and |
| 1456 // Flush calls. In Go 1.0, rw.WriteHeader immediately flushed the |
| 1457 // (*response).header to the wire. In Go 1.1, the actual wire flush is |
| 1458 // delayed, so we could maybe tack on a Content-Length and better |
| 1459 // Content-Type after we see more (or all) of the output. To preserve |
| 1460 // compatibility with Go 1, we need to be careful to track which |
| 1461 // headers were live at the time of WriteHeader, so we write the same |
| 1462 // ones, even if the handler modifies them (~erroneously) after the |
| 1463 // first Write. |
| 1464 func TestHeaderToWire(t *testing.T) { |
| 1465 req := []byte(strings.Replace(`GET / HTTP/1.1 |
| 1466 Host: golang.org |
| 1467 |
| 1468 `, "\n", "\r\n", -1)) |
| 1469 |
| 1470 tests := []struct { |
| 1471 name string |
| 1472 handler func(ResponseWriter, *Request) |
| 1473 check func(output string) error |
| 1474 }{ |
| 1475 { |
| 1476 name: "write without Header", |
| 1477 handler: func(rw ResponseWriter, r *Request) { |
| 1478 rw.Write([]byte("hello world")) |
| 1479 }, |
| 1480 check: func(got string) error { |
| 1481 if !strings.Contains(got, "Content-Length:") { |
| 1482 return errors.New("no content-length") |
| 1483 } |
| 1484 if !strings.Contains(got, "Content-Type: text/pl
ain") { |
| 1485 return errors.New("no content-length") |
| 1486 } |
| 1487 return nil |
| 1488 }, |
| 1489 }, |
| 1490 { |
| 1491 name: "Header mutation before write", |
| 1492 handler: func(rw ResponseWriter, r *Request) { |
| 1493 h := rw.Header() |
| 1494 h.Set("Content-Type", "some/type") |
| 1495 rw.Write([]byte("hello world")) |
| 1496 h.Set("Too-Late", "bogus") |
| 1497 }, |
| 1498 check: func(got string) error { |
| 1499 if !strings.Contains(got, "Content-Length:") { |
| 1500 return errors.New("no content-length") |
| 1501 } |
| 1502 if !strings.Contains(got, "Content-Type: some/ty
pe") { |
| 1503 return errors.New("wrong content-type") |
| 1504 } |
| 1505 if strings.Contains(got, "Too-Late") { |
| 1506 return errors.New("don't want too-late h
eader") |
| 1507 } |
| 1508 return nil |
| 1509 }, |
| 1510 }, |
| 1511 { |
| 1512 name: "write then useless Header mutation", |
| 1513 handler: func(rw ResponseWriter, r *Request) { |
| 1514 rw.Write([]byte("hello world")) |
| 1515 rw.Header().Set("Too-Late", "Write already wrote
headers") |
| 1516 }, |
| 1517 check: func(got string) error { |
| 1518 if strings.Contains(got, "Too-Late") { |
| 1519 return errors.New("header appeared from
after WriteHeader") |
| 1520 } |
| 1521 return nil |
| 1522 }, |
| 1523 }, |
| 1524 { |
| 1525 name: "flush then write", |
| 1526 handler: func(rw ResponseWriter, r *Request) { |
| 1527 rw.(Flusher).Flush() |
| 1528 rw.Write([]byte("post-flush")) |
| 1529 rw.Header().Set("Too-Late", "Write already wrote
headers") |
| 1530 }, |
| 1531 check: func(got string) error { |
| 1532 if !strings.Contains(got, "Transfer-Encoding: ch
unked") { |
| 1533 return errors.New("not chunked") |
| 1534 } |
| 1535 if strings.Contains(got, "Too-Late") { |
| 1536 return errors.New("header appeared from
after WriteHeader") |
| 1537 } |
| 1538 return nil |
| 1539 }, |
| 1540 }, |
| 1541 { |
| 1542 name: "header then flush", |
| 1543 handler: func(rw ResponseWriter, r *Request) { |
| 1544 rw.Header().Set("Content-Type", "some/type") |
| 1545 rw.(Flusher).Flush() |
| 1546 rw.Write([]byte("post-flush")) |
| 1547 rw.Header().Set("Too-Late", "Write already wrote
headers") |
| 1548 }, |
| 1549 check: func(got string) error { |
| 1550 if !strings.Contains(got, "Transfer-Encoding: ch
unked") { |
| 1551 return errors.New("not chunked") |
| 1552 } |
| 1553 if strings.Contains(got, "Too-Late") { |
| 1554 return errors.New("header appeared from
after WriteHeader") |
| 1555 } |
| 1556 if !strings.Contains(got, "Content-Type: some/ty
pe") { |
| 1557 return errors.New("wrong content-length"
) |
| 1558 } |
| 1559 return nil |
| 1560 }, |
| 1561 }, |
| 1562 { |
| 1563 name: "sniff-on-first-write content-type", |
| 1564 handler: func(rw ResponseWriter, r *Request) { |
| 1565 rw.Write([]byte("<html><head></head><body>some h
tml</body></html>")) |
| 1566 rw.Header().Set("Content-Type", "x/wrong") |
| 1567 }, |
| 1568 check: func(got string) error { |
| 1569 if !strings.Contains(got, "Content-Type: text/ht
ml") { |
| 1570 return errors.New("wrong content-length;
want html") |
| 1571 } |
| 1572 return nil |
| 1573 }, |
| 1574 }, |
| 1575 { |
| 1576 name: "explicit content-type wins", |
| 1577 handler: func(rw ResponseWriter, r *Request) { |
| 1578 rw.Header().Set("Content-Type", "some/type") |
| 1579 rw.Write([]byte("<html><head></head><body>some h
tml</body></html>")) |
| 1580 }, |
| 1581 check: func(got string) error { |
| 1582 if !strings.Contains(got, "Content-Type: some/ty
pe") { |
| 1583 return errors.New("wrong content-length;
want html") |
| 1584 } |
| 1585 return nil |
| 1586 }, |
| 1587 }, |
| 1588 { |
| 1589 name: "empty handler", |
| 1590 handler: func(rw ResponseWriter, r *Request) { |
| 1591 }, |
| 1592 check: func(got string) error { |
| 1593 if !strings.Contains(got, "Content-Type: text/pl
ain") { |
| 1594 return errors.New("wrong content-length;
want text/plain") |
| 1595 } |
| 1596 if !strings.Contains(got, "Content-Length: 0") { |
| 1597 return errors.New("want 0 content-length
") |
| 1598 } |
| 1599 return nil |
| 1600 }, |
| 1601 }, |
| 1602 { |
| 1603 name: "only Header, no write", |
| 1604 handler: func(rw ResponseWriter, r *Request) { |
| 1605 rw.Header().Set("Some-Header", "some-value") |
| 1606 }, |
| 1607 check: func(got string) error { |
| 1608 if !strings.Contains(got, "Some-Header") { |
| 1609 return errors.New("didn't get header") |
| 1610 } |
| 1611 return nil |
| 1612 }, |
| 1613 }, |
| 1614 { |
| 1615 name: "WriteHeader call", |
| 1616 handler: func(rw ResponseWriter, r *Request) { |
| 1617 rw.WriteHeader(404) |
| 1618 rw.Header().Set("Too-Late", "some-value") |
| 1619 }, |
| 1620 check: func(got string) error { |
| 1621 if !strings.Contains(got, "404") { |
| 1622 return errors.New("wrong status") |
| 1623 } |
| 1624 if strings.Contains(got, "Some-Header") { |
| 1625 return errors.New("shouldn't have seen T
oo-Late") |
| 1626 } |
| 1627 return nil |
| 1628 }, |
| 1629 }, |
| 1630 } |
| 1631 for _, tc := range tests { |
| 1632 var output bytes.Buffer |
| 1633 conn := &rwTestConn{ |
| 1634 Reader: bytes.NewReader(req), |
| 1635 Writer: &output, |
| 1636 closec: make(chan bool, 1), |
| 1637 } |
| 1638 ln := &oneConnListener{conn: conn} |
| 1639 go Serve(ln, HandlerFunc(tc.handler)) |
| 1640 <-conn.closec |
| 1641 if err := tc.check(output.String()); err != nil { |
| 1642 t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, outp
ut.Bytes()) |
| 1643 } |
| 1644 } |
| 1645 } |
| 1646 |
1454 // goTimeout runs f, failing t if f takes more than ns to complete. | 1647 // goTimeout runs f, failing t if f takes more than ns to complete. |
1455 func goTimeout(t *testing.T, d time.Duration, f func()) { | 1648 func goTimeout(t *testing.T, d time.Duration, f func()) { |
1456 ch := make(chan bool, 2) | 1649 ch := make(chan bool, 2) |
1457 timer := time.AfterFunc(d, func() { | 1650 timer := time.AfterFunc(d, func() { |
1458 t.Errorf("Timeout expired after %v", d) | 1651 t.Errorf("Timeout expired after %v", d) |
1459 ch <- true | 1652 ch <- true |
1460 }) | 1653 }) |
1461 defer timer.Stop() | 1654 defer timer.Stop() |
1462 go func() { | 1655 go func() { |
1463 defer func() { ch <- true }() | 1656 defer func() { ch <- true }() |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1703 rw.Header().Set("Content-Type", "text/html; charset=utf-8") | 1896 rw.Header().Set("Content-Type", "text/html; charset=utf-8") |
1704 rw.Write(res) | 1897 rw.Write(res) |
1705 }) | 1898 }) |
1706 ln := &oneConnListener{conn: conn} | 1899 ln := &oneConnListener{conn: conn} |
1707 go Serve(ln, handler) | 1900 go Serve(ln, handler) |
1708 <-conn.closec | 1901 <-conn.closec |
1709 if b.N != handled { | 1902 if b.N != handled { |
1710 b.Errorf("b.N=%d but handled %d", b.N, handled) | 1903 b.Errorf("b.N=%d but handled %d", b.N, handled) |
1711 } | 1904 } |
1712 } | 1905 } |
| 1906 |
| 1907 // same as above, but representing the most simple possible request |
| 1908 // and handler. Notably: the handler does not call rw.Header(). |
| 1909 func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) { |
| 1910 b.ReportAllocs() |
| 1911 |
| 1912 req := []byte(strings.Replace(`GET / HTTP/1.1 |
| 1913 Host: golang.org |
| 1914 |
| 1915 `, "\n", "\r\n", -1)) |
| 1916 res := []byte("Hello world!\n") |
| 1917 |
| 1918 conn := &rwTestConn{ |
| 1919 Reader: &repeatReader{content: req, count: b.N}, |
| 1920 Writer: ioutil.Discard, |
| 1921 closec: make(chan bool, 1), |
| 1922 } |
| 1923 handled := 0 |
| 1924 handler := HandlerFunc(func(rw ResponseWriter, r *Request) { |
| 1925 handled++ |
| 1926 rw.Write(res) |
| 1927 }) |
| 1928 ln := &oneConnListener{conn: conn} |
| 1929 go Serve(ln, handler) |
| 1930 <-conn.closec |
| 1931 if b.N != handled { |
| 1932 b.Errorf("b.N=%d but handled %d", b.N, handled) |
| 1933 } |
| 1934 } |
LEFT | RIGHT |