1
2
3
4
5 package http2
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "io/ioutil"
12 "net/http"
13 "os"
14 "path/filepath"
15 "regexp"
16 "strings"
17 "testing"
18 "time"
19
20 "golang.org/x/net/http2/hpack"
21 )
22
23 var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.")
24
25 func condSkipFailingTest(t *testing.T) {
26 if !*knownFailing {
27 t.Skip("Skipping known-failing test without --known_failing")
28 }
29 }
30
31 func init() {
32 inTests = true
33 DebugGoroutines = true
34 flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging")
35 }
36
37 func TestSettingString(t *testing.T) {
38 tests := []struct {
39 s Setting
40 want string
41 }{
42 {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"},
43 {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"},
44 }
45 for i, tt := range tests {
46 got := fmt.Sprint(tt.s)
47 if got != tt.want {
48 t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want)
49 }
50 }
51 }
52
53 type twriter struct {
54 t testing.TB
55 st *serverTester
56 }
57
58 func (w twriter) Write(p []byte) (n int, err error) {
59 if w.st != nil {
60 ps := string(p)
61 for _, phrase := range w.st.logFilter {
62 if strings.Contains(ps, phrase) {
63 return len(p), nil
64 }
65 }
66 }
67 w.t.Logf("%s", p)
68 return len(p), nil
69 }
70
71
72 func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte {
73 var buf bytes.Buffer
74 enc := hpack.NewEncoder(&buf)
75 for len(headers) > 0 {
76 k, v := headers[0], headers[1]
77 headers = headers[2:]
78 if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
79 t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
80 }
81 }
82 return buf.Bytes()
83 }
84
85 type puppetCommand struct {
86 fn func(w http.ResponseWriter, r *http.Request)
87 done chan<- bool
88 }
89
90 type handlerPuppet struct {
91 ch chan puppetCommand
92 }
93
94 func newHandlerPuppet() *handlerPuppet {
95 return &handlerPuppet{
96 ch: make(chan puppetCommand),
97 }
98 }
99
100 func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
101 for cmd := range p.ch {
102 cmd.fn(w, r)
103 cmd.done <- true
104 }
105 }
106
107 func (p *handlerPuppet) done() { close(p.ch) }
108 func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
109 done := make(chan bool)
110 p.ch <- puppetCommand{fn, done}
111 <-done
112 }
113
114 func cleanDate(res *http.Response) {
115 if d := res.Header["Date"]; len(d) == 1 {
116 d[0] = "XXX"
117 }
118 }
119
120 func TestSorterPoolAllocs(t *testing.T) {
121 ss := []string{"a", "b", "c"}
122 h := http.Header{
123 "a": nil,
124 "b": nil,
125 "c": nil,
126 }
127 sorter := new(sorter)
128
129 if allocs := testing.AllocsPerRun(100, func() {
130 sorter.SortStrings(ss)
131 }); allocs >= 1 {
132 t.Logf("SortStrings allocs = %v; want <1", allocs)
133 }
134
135 if allocs := testing.AllocsPerRun(5, func() {
136 if len(sorter.Keys(h)) != 3 {
137 t.Fatal("wrong result")
138 }
139 }); allocs > 0 {
140 t.Logf("Keys allocs = %v; want <1", allocs)
141 }
142 }
143
144
145
146
147 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
148 deadline := time.Now().Add(waitFor)
149 for time.Now().Before(deadline) {
150 if fn() {
151 return true
152 }
153 time.Sleep(checkEvery)
154 }
155 return false
156 }
157
158
159 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
160 deadline := time.Now().Add(waitFor)
161 var err error
162 for time.Now().Before(deadline) {
163 if err = fn(); err == nil {
164 return nil
165 }
166 time.Sleep(checkEvery)
167 }
168 return err
169 }
170
171 func equalError(a, b error) bool {
172 if a == nil {
173 return b == nil
174 }
175 if b == nil {
176 return a == nil
177 }
178 return a.Error() == b.Error()
179 }
180
181
182
183
184 func TestConfigureServerIdleTimeout_Go18(t *testing.T) {
185 const timeout = 5 * time.Second
186 const notThisOne = 1 * time.Second
187
188
189 {
190 s1 := &http.Server{
191 IdleTimeout: timeout,
192 ReadTimeout: notThisOne,
193 }
194 s2 := &Server{}
195 if err := ConfigureServer(s1, s2); err != nil {
196 t.Fatal(err)
197 }
198 if s2.IdleTimeout != timeout {
199 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
200 }
201 }
202
203
204 {
205 s1 := &http.Server{
206 ReadTimeout: timeout,
207 }
208 s2 := &Server{}
209 if err := ConfigureServer(s1, s2); err != nil {
210 t.Fatal(err)
211 }
212 if s2.IdleTimeout != timeout {
213 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
214 }
215 }
216
217
218 {
219 s1 := &http.Server{
220 IdleTimeout: notThisOne,
221 }
222 s2 := &Server{
223 IdleTimeout: timeout,
224 }
225 if err := ConfigureServer(s1, s2); err != nil {
226 t.Fatal(err)
227 }
228 if s2.IdleTimeout != timeout {
229 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
230 }
231 }
232 }
233
234 var forbiddenStringsFunctions = map[string]bool{
235
236 "EqualFold": true,
237 "Title": true,
238 "ToLower": true,
239 "ToLowerSpecial": true,
240 "ToTitle": true,
241 "ToTitleSpecial": true,
242 "ToUpper": true,
243 "ToUpperSpecial": true,
244
245
246 "Fields": true,
247 "TrimSpace": true,
248 }
249
250
251
252
253 func TestNoUnicodeStrings(t *testing.T) {
254 re := regexp.MustCompile(`(strings|bytes).([A-Za-z]+)`)
255 if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
256 if err != nil {
257 t.Fatal(err)
258 }
259
260 if path == "h2i" || path == "h2c" {
261 return filepath.SkipDir
262 }
263 if !strings.HasSuffix(path, ".go") ||
264 strings.HasSuffix(path, "_test.go") ||
265 path == "ascii.go" || info.IsDir() {
266 return nil
267 }
268
269 contents, err := ioutil.ReadFile(path)
270 if err != nil {
271 t.Fatal(err)
272 }
273 for lineNum, line := range strings.Split(string(contents), "\n") {
274 for _, match := range re.FindAllStringSubmatch(line, -1) {
275 if !forbiddenStringsFunctions[match[2]] {
276 continue
277 }
278 t.Errorf("disallowed call to %s at %s:%d", match[0], path, lineNum+1)
279 }
280 }
281
282 return nil
283 }); err != nil {
284 t.Fatal(err)
285 }
286 }
287
View as plain text