Skip to content

Commit 931fe39

Browse files
author
Bryan C. Mills
committed
net/http: await state traces earlier in TestServerConnState
This approach attempts to ensure that the log for each connection is complete before the next sequence of states begins. Updates #32329 Change-Id: I25150d3ceab6568af56a40d2b14b5f544dc87f61 Reviewed-on: https://go-review.googlesource.com/c/go/+/210717 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent ecde0bf commit 931fe39

File tree

1 file changed

+76
-69
lines changed

1 file changed

+76
-69
lines changed

src/net/http/serve_test.go

+76-69
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434
"regexp"
3535
"runtime"
3636
"runtime/debug"
37-
"sort"
3837
"strconv"
3938
"strings"
4039
"sync"
@@ -4116,35 +4115,77 @@ func TestServerConnState(t *testing.T) {
41164115
panic("intentional panic")
41174116
},
41184117
}
4118+
4119+
// A stateLog is a log of states over the lifetime of a connection.
4120+
type stateLog struct {
4121+
active net.Conn // The connection for which the log is recorded; set to the first connection seen in StateNew.
4122+
got []ConnState
4123+
want []ConnState
4124+
complete chan<- struct{} // If non-nil, closed when either 'got' is equal to 'want', or 'got' is no longer a prefix of 'want'.
4125+
}
4126+
activeLog := make(chan *stateLog, 1)
4127+
4128+
// wantLog invokes doRequests, then waits for the resulting connection to
4129+
// either pass through the sequence of states in want or enter a state outside
4130+
// of that sequence.
4131+
wantLog := func(doRequests func(), want ...ConnState) {
4132+
t.Helper()
4133+
complete := make(chan struct{})
4134+
activeLog <- &stateLog{want: want, complete: complete}
4135+
4136+
doRequests()
4137+
4138+
timer := time.NewTimer(5 * time.Second)
4139+
select {
4140+
case <-timer.C:
4141+
t.Errorf("Timed out waiting for connection to change state.")
4142+
case <-complete:
4143+
timer.Stop()
4144+
}
4145+
sl := <-activeLog
4146+
if !reflect.DeepEqual(sl.got, sl.want) {
4147+
t.Errorf("Request(s) produced unexpected state sequence.\nGot: %v\nWant: %v", sl.got, sl.want)
4148+
}
4149+
// Don't return sl to activeLog: we don't expect any further states after
4150+
// this point, and want to keep the ConnState callback blocked until the
4151+
// next call to wantLog.
4152+
}
4153+
41194154
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
41204155
handler[r.URL.Path](w, r)
41214156
}))
4122-
defer ts.Close()
4123-
4124-
var mu sync.Mutex // guard stateLog and connID
4125-
var stateLog = map[int][]ConnState{}
4126-
var connID = map[net.Conn]int{}
4157+
defer func() {
4158+
activeLog <- &stateLog{} // If the test failed, allow any remaining ConnState callbacks to complete.
4159+
ts.Close()
4160+
}()
41274161

41284162
ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
41294163
ts.Config.ConnState = func(c net.Conn, state ConnState) {
41304164
if c == nil {
41314165
t.Errorf("nil conn seen in state %s", state)
41324166
return
41334167
}
4134-
mu.Lock()
4135-
defer mu.Unlock()
4136-
id, ok := connID[c]
4137-
if !ok {
4138-
id = len(connID) + 1
4139-
connID[c] = id
4168+
sl := <-activeLog
4169+
if sl.active == nil && state == StateNew {
4170+
sl.active = c
4171+
} else if sl.active != c {
4172+
t.Errorf("unexpected conn in state %s", state)
4173+
activeLog <- sl
4174+
return
41404175
}
4141-
stateLog[id] = append(stateLog[id], state)
4176+
sl.got = append(sl.got, state)
4177+
if sl.complete != nil && (len(sl.got) >= len(sl.want) || !reflect.DeepEqual(sl.got, sl.want[:len(sl.got)])) {
4178+
close(sl.complete)
4179+
sl.complete = nil
4180+
}
4181+
activeLog <- sl
41424182
}
4143-
ts.Start()
41444183

4184+
ts.Start()
41454185
c := ts.Client()
41464186

41474187
mustGet := func(url string, headers ...string) {
4188+
t.Helper()
41484189
req, err := NewRequest("GET", url, nil)
41494190
if err != nil {
41504191
t.Fatal(err)
@@ -4165,26 +4206,33 @@ func TestServerConnState(t *testing.T) {
41654206
}
41664207
}
41674208

4168-
mustGet(ts.URL + "/")
4169-
mustGet(ts.URL + "/close")
4209+
wantLog(func() {
4210+
mustGet(ts.URL + "/")
4211+
mustGet(ts.URL + "/close")
4212+
}, StateNew, StateActive, StateIdle, StateActive, StateClosed)
41704213

4171-
mustGet(ts.URL + "/")
4172-
mustGet(ts.URL+"/", "Connection", "close")
4214+
wantLog(func() {
4215+
mustGet(ts.URL + "/")
4216+
mustGet(ts.URL+"/", "Connection", "close")
4217+
}, StateNew, StateActive, StateIdle, StateActive, StateClosed)
41734218

4174-
mustGet(ts.URL + "/hijack")
4175-
mustGet(ts.URL + "/hijack-panic")
4219+
wantLog(func() {
4220+
mustGet(ts.URL + "/hijack")
4221+
}, StateNew, StateActive, StateHijacked)
41764222

4177-
// New->Closed
4178-
{
4223+
wantLog(func() {
4224+
mustGet(ts.URL + "/hijack-panic")
4225+
}, StateNew, StateActive, StateHijacked)
4226+
4227+
wantLog(func() {
41794228
c, err := net.Dial("tcp", ts.Listener.Addr().String())
41804229
if err != nil {
41814230
t.Fatal(err)
41824231
}
41834232
c.Close()
4184-
}
4233+
}, StateNew, StateClosed)
41854234

4186-
// New->Active->Closed
4187-
{
4235+
wantLog(func() {
41884236
c, err := net.Dial("tcp", ts.Listener.Addr().String())
41894237
if err != nil {
41904238
t.Fatal(err)
@@ -4194,10 +4242,9 @@ func TestServerConnState(t *testing.T) {
41944242
}
41954243
c.Read(make([]byte, 1)) // block until server hangs up on us
41964244
c.Close()
4197-
}
4245+
}, StateNew, StateActive, StateClosed)
41984246

4199-
// New->Idle->Closed
4200-
{
4247+
wantLog(func() {
42014248
c, err := net.Dial("tcp", ts.Listener.Addr().String())
42024249
if err != nil {
42034250
t.Fatal(err)
@@ -4213,47 +4260,7 @@ func TestServerConnState(t *testing.T) {
42134260
t.Fatal(err)
42144261
}
42154262
c.Close()
4216-
}
4217-
4218-
want := map[int][]ConnState{
4219-
1: {StateNew, StateActive, StateIdle, StateActive, StateClosed},
4220-
2: {StateNew, StateActive, StateIdle, StateActive, StateClosed},
4221-
3: {StateNew, StateActive, StateHijacked},
4222-
4: {StateNew, StateActive, StateHijacked},
4223-
5: {StateNew, StateClosed},
4224-
6: {StateNew, StateActive, StateClosed},
4225-
7: {StateNew, StateActive, StateIdle, StateClosed},
4226-
}
4227-
logString := func(m map[int][]ConnState) string {
4228-
var b bytes.Buffer
4229-
var keys []int
4230-
for id := range m {
4231-
keys = append(keys, id)
4232-
}
4233-
sort.Ints(keys)
4234-
for _, id := range keys {
4235-
fmt.Fprintf(&b, "Conn %d: ", id)
4236-
for _, s := range m[id] {
4237-
fmt.Fprintf(&b, "%s ", s)
4238-
}
4239-
b.WriteString("\n")
4240-
}
4241-
return b.String()
4242-
}
4243-
4244-
for i := 0; i < 5; i++ {
4245-
time.Sleep(time.Duration(i) * 50 * time.Millisecond)
4246-
mu.Lock()
4247-
match := reflect.DeepEqual(stateLog, want)
4248-
mu.Unlock()
4249-
if match {
4250-
return
4251-
}
4252-
}
4253-
4254-
mu.Lock()
4255-
t.Errorf("Unexpected events.\nGot log:\n%s\n Want:\n%s\n", logString(stateLog), logString(want))
4256-
mu.Unlock()
4263+
}, StateNew, StateActive, StateIdle, StateClosed)
42574264
}
42584265

42594266
func TestServerKeepAlivesEnabled(t *testing.T) {

0 commit comments

Comments
 (0)