http2_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2014 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. // See https://code.google.com/p/go/source/browse/CONTRIBUTORS
  5. // Licensed under the same terms as Go itself:
  6. // https://code.google.com/p/go/source/browse/LICENSE
  7. package http2
  8. import (
  9. "crypto/tls"
  10. "errors"
  11. "fmt"
  12. "io"
  13. "net/http"
  14. "net/http/httptest"
  15. "os/exec"
  16. "strconv"
  17. "strings"
  18. "sync/atomic"
  19. "testing"
  20. "time"
  21. )
  22. func TestServer(t *testing.T) {
  23. ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  24. w.Header().Set("Foo", "Bar")
  25. }))
  26. ConfigureServer(ts.Config, &Server{})
  27. ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
  28. ts.StartTLS()
  29. defer ts.Close()
  30. t.Logf("Running test server at: %s", ts.URL)
  31. cc, err := tls.Dial("tcp", ts.Listener.Addr().String(), &tls.Config{
  32. InsecureSkipVerify: true,
  33. NextProtos: []string{npnProto},
  34. })
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. defer cc.Close()
  39. mustWrite(t, cc, clientPreface)
  40. fr := NewFramer(cc, cc)
  41. if err := fr.WriteSettings(); err != nil {
  42. t.Fatal(err)
  43. }
  44. // Expect first Settings frame.
  45. {
  46. f, err := fr.ReadFrame()
  47. if err != nil {
  48. t.Fatal(err)
  49. }
  50. sf, ok := f.(*SettingsFrame)
  51. if !ok {
  52. t.Fatalf("Received a %T, not a Settings frame from the server", f)
  53. }
  54. sf.ForeachSetting(func(s Setting) {
  55. t.Logf("Server sent setting %v = %v", s.ID, s.Val)
  56. })
  57. }
  58. // And expect an ACK of our settings.
  59. {
  60. f, err := fr.ReadFrame()
  61. if err != nil {
  62. t.Fatal(err)
  63. }
  64. sf, ok := f.(*SettingsFrame)
  65. if !ok {
  66. t.Fatalf("Received a %T, not a Settings ack frame from the server", f)
  67. }
  68. if !sf.Header().Flags.Has(FlagSettingsAck) {
  69. t.Fatal("Settings Frame didn't have ACK set")
  70. }
  71. }
  72. // TODO: table-itize steps, write request (HEADERS frame), read response.
  73. }
  74. func mustWrite(t *testing.T, w io.Writer, p []byte) {
  75. n, err := w.Write(p)
  76. const maxLen = 80
  77. l := len(p)
  78. if len(p) > maxLen {
  79. p = p[:maxLen]
  80. }
  81. if err != nil {
  82. t.Fatalf("Error writing %d bytes (%q): %v", l, p, err)
  83. }
  84. if n != len(p) {
  85. t.Fatalf("Only wrote %d of %d bytes (%q)", n, l, p)
  86. }
  87. }
  88. func TestServerWithCurl(t *testing.T) {
  89. requireCurl(t)
  90. ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  91. // TODO: add a bunch of different tests with different
  92. // behavior, as a function of r or a table.
  93. // -- with request body, without.
  94. // -- no interaction with w.
  95. // -- panic
  96. // -- modify Header only, but no writes or writeheader (this test)
  97. // -- WriteHeader only
  98. // -- Write only
  99. // -- WriteString
  100. // -- both
  101. // -- huge headers over a frame size so we get continuation headers.
  102. // Look at net/http's Server tests for inspiration.
  103. w.Header().Set("Foo", "Bar")
  104. }))
  105. ConfigureServer(ts.Config, &Server{})
  106. ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
  107. ts.StartTLS()
  108. defer ts.Close()
  109. var gotConn int32
  110. testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
  111. t.Logf("Running test server for curl to hit at: %s", ts.URL)
  112. container := curl(t, "--silent", "--http2", "--insecure", "-v", ts.URL)
  113. defer kill(container)
  114. resc := make(chan interface{}, 1)
  115. go func() {
  116. res, err := dockerLogs(container)
  117. if err != nil {
  118. resc <- err
  119. } else {
  120. resc <- res
  121. }
  122. }()
  123. select {
  124. case res := <-resc:
  125. if err, ok := res.(error); ok {
  126. t.Fatal(err)
  127. }
  128. if !strings.Contains(string(res.([]byte)), "< foo:Bar") {
  129. t.Errorf("didn't see foo:Bar header")
  130. t.Logf("Got: %s", res)
  131. }
  132. case <-time.After(3 * time.Second):
  133. t.Errorf("timeout waiting for curl")
  134. }
  135. if atomic.LoadInt32(&gotConn) == 0 {
  136. t.Error("never saw an http2 connection")
  137. }
  138. }
  139. func dockerLogs(container string) ([]byte, error) {
  140. out, err := exec.Command("docker", "wait", container).CombinedOutput()
  141. if err != nil {
  142. return out, err
  143. }
  144. exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
  145. if err != nil {
  146. return out, errors.New("unexpected exit status from docker wait")
  147. }
  148. out, err = exec.Command("docker", "logs", container).CombinedOutput()
  149. exec.Command("docker", "rm", container).Run()
  150. if err == nil && exitStatus != 0 {
  151. err = fmt.Errorf("exit status %d", exitStatus)
  152. }
  153. return out, err
  154. }
  155. func kill(container string) {
  156. exec.Command("docker", "kill", container).Run()
  157. exec.Command("docker", "rm", container).Run()
  158. }
  159. // Verify that curl has http2.
  160. func requireCurl(t *testing.T) {
  161. out, err := dockerLogs(curl(t, "--version"))
  162. if err != nil {
  163. t.Skipf("failed to determine curl features; skipping test")
  164. }
  165. if !strings.Contains(string(out), "HTTP2") {
  166. t.Skip("curl doesn't support HTTP2; skipping test")
  167. }
  168. }
  169. func curl(t *testing.T, args ...string) (container string) {
  170. out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).CombinedOutput()
  171. if err != nil {
  172. t.Skipf("Failed to run curl in docker: %v, %s", err, out)
  173. }
  174. return strings.TrimSpace(string(out))
  175. }