http2_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. "bytes"
  10. "errors"
  11. "flag"
  12. "fmt"
  13. "net/http"
  14. "os/exec"
  15. "strconv"
  16. "strings"
  17. "testing"
  18. "github.com/bradfitz/http2/hpack"
  19. )
  20. func init() {
  21. DebugGoroutines = true
  22. flag.BoolVar(&VerboseLogs, "verboseh2", false, "Verbose HTTP/2 debug logging")
  23. }
  24. func TestSettingString(t *testing.T) {
  25. tests := []struct {
  26. s Setting
  27. want string
  28. }{
  29. {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"},
  30. }
  31. for i, tt := range tests {
  32. got := fmt.Sprint(tt.s)
  33. if got != tt.want {
  34. t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want)
  35. }
  36. }
  37. }
  38. type twriter struct {
  39. t testing.TB
  40. st *serverTester // optional
  41. }
  42. func (w twriter) Write(p []byte) (n int, err error) {
  43. if w.st != nil {
  44. ps := string(p)
  45. for _, phrase := range w.st.logFilter {
  46. if strings.Contains(ps, phrase) {
  47. return len(p), nil // no logging
  48. }
  49. }
  50. }
  51. w.t.Logf("%s", p)
  52. return len(p), nil
  53. }
  54. // encodeHeader encodes headers and returns their HPACK bytes. headers
  55. // must contain an even number of key/value pairs. There may be
  56. // multiple pairs for keys (e.g. "cookie"). The :method, :path, and
  57. // :scheme headers default to GET, / and https.
  58. func encodeHeader(t *testing.T, headers ...string) []byte {
  59. pseudoCount := map[string]int{}
  60. if len(headers)%2 == 1 {
  61. panic("odd number of kv args")
  62. }
  63. keys := []string{":method", ":path", ":scheme"}
  64. vals := map[string][]string{
  65. ":method": {"GET"},
  66. ":path": {"/"},
  67. ":scheme": {"https"},
  68. }
  69. for len(headers) > 0 {
  70. k, v := headers[0], headers[1]
  71. headers = headers[2:]
  72. if _, ok := vals[k]; !ok {
  73. keys = append(keys, k)
  74. }
  75. if strings.HasPrefix(k, ":") {
  76. pseudoCount[k]++
  77. if pseudoCount[k] == 1 {
  78. vals[k] = []string{v}
  79. } else {
  80. // Allows testing of invalid headers w/ dup pseudo fields.
  81. vals[k] = append(vals[k], v)
  82. }
  83. } else {
  84. vals[k] = append(vals[k], v)
  85. }
  86. }
  87. var buf bytes.Buffer
  88. enc := hpack.NewEncoder(&buf)
  89. for _, k := range keys {
  90. for _, v := range vals[k] {
  91. if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
  92. t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
  93. }
  94. }
  95. }
  96. return buf.Bytes()
  97. }
  98. // like encodeHeader, but don't add implicit psuedo headers.
  99. func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte {
  100. var buf bytes.Buffer
  101. enc := hpack.NewEncoder(&buf)
  102. for len(headers) > 0 {
  103. k, v := headers[0], headers[1]
  104. headers = headers[2:]
  105. if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
  106. t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
  107. }
  108. }
  109. return buf.Bytes()
  110. }
  111. // Verify that curl has http2.
  112. func requireCurl(t *testing.T) {
  113. out, err := dockerLogs(curl(t, "--version"))
  114. if err != nil {
  115. t.Skipf("failed to determine curl features; skipping test")
  116. }
  117. if !strings.Contains(string(out), "HTTP2") {
  118. t.Skip("curl doesn't support HTTP2; skipping test")
  119. }
  120. }
  121. func curl(t *testing.T, args ...string) (container string) {
  122. out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).CombinedOutput()
  123. if err != nil {
  124. t.Skipf("Failed to run curl in docker: %v, %s", err, out)
  125. }
  126. return strings.TrimSpace(string(out))
  127. }
  128. type puppetCommand struct {
  129. fn func(w http.ResponseWriter, r *http.Request)
  130. done chan<- bool
  131. }
  132. type handlerPuppet struct {
  133. ch chan puppetCommand
  134. }
  135. func newHandlerPuppet() *handlerPuppet {
  136. return &handlerPuppet{
  137. ch: make(chan puppetCommand),
  138. }
  139. }
  140. func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
  141. for cmd := range p.ch {
  142. cmd.fn(w, r)
  143. cmd.done <- true
  144. }
  145. }
  146. func (p *handlerPuppet) done() { close(p.ch) }
  147. func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
  148. done := make(chan bool)
  149. p.ch <- puppetCommand{fn, done}
  150. <-done
  151. }
  152. func dockerLogs(container string) ([]byte, error) {
  153. out, err := exec.Command("docker", "wait", container).CombinedOutput()
  154. if err != nil {
  155. return out, err
  156. }
  157. exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
  158. if err != nil {
  159. return out, errors.New("unexpected exit status from docker wait")
  160. }
  161. out, err = exec.Command("docker", "logs", container).CombinedOutput()
  162. exec.Command("docker", "rm", container).Run()
  163. if err == nil && exitStatus != 0 {
  164. err = fmt.Errorf("exit status %d", exitStatus)
  165. }
  166. return out, err
  167. }
  168. func kill(container string) {
  169. exec.Command("docker", "kill", container).Run()
  170. exec.Command("docker", "rm", container).Run()
  171. }