http2_test.go 5.6 KB

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