Source file
src/net/http/main_test.go
1
2
3
4
5 package http_test
6
7 import (
8 "fmt"
9 "io"
10 "log"
11 "net/http"
12 "os"
13 "runtime"
14 "slices"
15 "strings"
16 "testing"
17 "time"
18 )
19
20 var quietLog = log.New(io.Discard, "", 0)
21
22 func TestMain(m *testing.M) {
23 *http.MaxWriteWaitBeforeConnReuse = 60 * time.Minute
24 v := m.Run()
25 if v == 0 && goroutineLeaked() {
26 os.Exit(1)
27 }
28 os.Exit(v)
29 }
30
31 func interestingGoroutines() (gs []string) {
32 buf := make([]byte, 2<<20)
33 buf = buf[:runtime.Stack(buf, true)]
34 for _, g := range strings.Split(string(buf), "\n\n") {
35 _, stack, _ := strings.Cut(g, "\n")
36 stack = strings.TrimSpace(stack)
37 if stack == "" ||
38 strings.Contains(stack, "testing.(*M).before.func1") ||
39 strings.Contains(stack, "os/signal.signal_recv") ||
40 strings.Contains(stack, "created by net.startServer") ||
41 strings.Contains(stack, "created by testing.RunTests") ||
42 strings.Contains(stack, "closeWriteAndWait") ||
43 strings.Contains(stack, "testing.Main(") ||
44
45 strings.Contains(stack, "runtime.goexit") ||
46 strings.Contains(stack, "created by runtime.gc") ||
47 strings.Contains(stack, "interestingGoroutines") ||
48 strings.Contains(stack, "runtime.MHeap_Scavenger") {
49 continue
50 }
51 gs = append(gs, stack)
52 }
53 slices.Sort(gs)
54 return
55 }
56
57
58 func goroutineLeaked() bool {
59 if testing.Short() || runningBenchmarks() {
60
61
62 return false
63 }
64
65 var stackCount map[string]int
66 for i := 0; i < 5; i++ {
67 n := 0
68 stackCount = make(map[string]int)
69 gs := interestingGoroutines()
70 for _, g := range gs {
71 stackCount[g]++
72 n++
73 }
74 if n == 0 {
75 return false
76 }
77
78 time.Sleep(100 * time.Millisecond)
79 }
80 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
81 for stack, count := range stackCount {
82 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
83 }
84 return true
85 }
86
87
88
89
90 func setParallel(t *testing.T) {
91 if strings.Contains(t.Name(), "HTTP2") {
92 http.CondSkipHTTP2(t)
93 }
94 if testing.Short() {
95 t.Parallel()
96 }
97 }
98
99 func runningBenchmarks() bool {
100 for i, arg := range os.Args {
101 if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") {
102 return true
103 }
104 if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" {
105 return true
106 }
107 }
108 return false
109 }
110
111 var leakReported bool
112
113 func afterTest(t testing.TB) {
114 http.DefaultTransport.(*http.Transport).CloseIdleConnections()
115 if testing.Short() {
116 return
117 }
118 if leakReported {
119
120
121
122
123
124 return
125 }
126
127
128
129
130
131
132
133 leakReported = true
134
135 var bad string
136 badSubstring := map[string]string{
137 ").readLoop(": "a Transport",
138 ").writeLoop(": "a Transport",
139 "created by net/http/httptest.(*Server).Start": "an httptest.Server",
140 "timeoutHandler": "a TimeoutHandler",
141 "net.(*netFD).connect(": "a timing out dial",
142 ").noteClientGone(": "a closenotifier sender",
143 }
144 var stacks string
145 for i := 0; i < 2500; i++ {
146 bad = ""
147 stacks = strings.Join(interestingGoroutines(), "\n\n")
148 for substr, what := range badSubstring {
149 if strings.Contains(stacks, substr) {
150 bad = what
151 }
152 }
153 if bad == "" {
154 leakReported = false
155 return
156 }
157
158
159 time.Sleep(1 * time.Millisecond)
160 }
161 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
162 }
163
164
165
166 func waitCondition(t testing.TB, delay time.Duration, fn func(time.Duration) bool) {
167 t.Helper()
168 start := time.Now()
169 var since time.Duration
170 for !fn(since) {
171 time.Sleep(delay)
172 delay = 2*delay - (delay / 2)
173 since = time.Since(start)
174 }
175 }
176
View as plain text