@@ -34,7 +34,6 @@ import (
34
34
"regexp"
35
35
"runtime"
36
36
"runtime/debug"
37
- "sort"
38
37
"strconv"
39
38
"strings"
40
39
"sync"
@@ -4116,35 +4115,77 @@ func TestServerConnState(t *testing.T) {
4116
4115
panic ("intentional panic" )
4117
4116
},
4118
4117
}
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.\n Got: %v\n Want: %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
+
4119
4154
ts := httptest .NewUnstartedServer (HandlerFunc (func (w ResponseWriter , r * Request ) {
4120
4155
handler [r .URL .Path ](w , r )
4121
4156
}))
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
+ }()
4127
4161
4128
4162
ts .Config .ErrorLog = log .New (ioutil .Discard , "" , 0 )
4129
4163
ts .Config .ConnState = func (c net.Conn , state ConnState ) {
4130
4164
if c == nil {
4131
4165
t .Errorf ("nil conn seen in state %s" , state )
4132
4166
return
4133
4167
}
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
4140
4175
}
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
4142
4182
}
4143
- ts .Start ()
4144
4183
4184
+ ts .Start ()
4145
4185
c := ts .Client ()
4146
4186
4147
4187
mustGet := func (url string , headers ... string ) {
4188
+ t .Helper ()
4148
4189
req , err := NewRequest ("GET" , url , nil )
4149
4190
if err != nil {
4150
4191
t .Fatal (err )
@@ -4165,26 +4206,33 @@ func TestServerConnState(t *testing.T) {
4165
4206
}
4166
4207
}
4167
4208
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 )
4170
4213
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 )
4173
4218
4174
- mustGet (ts .URL + "/hijack" )
4175
- mustGet (ts .URL + "/hijack-panic" )
4219
+ wantLog (func () {
4220
+ mustGet (ts .URL + "/hijack" )
4221
+ }, StateNew , StateActive , StateHijacked )
4176
4222
4177
- // New->Closed
4178
- {
4223
+ wantLog (func () {
4224
+ mustGet (ts .URL + "/hijack-panic" )
4225
+ }, StateNew , StateActive , StateHijacked )
4226
+
4227
+ wantLog (func () {
4179
4228
c , err := net .Dial ("tcp" , ts .Listener .Addr ().String ())
4180
4229
if err != nil {
4181
4230
t .Fatal (err )
4182
4231
}
4183
4232
c .Close ()
4184
- }
4233
+ }, StateNew , StateClosed )
4185
4234
4186
- // New->Active->Closed
4187
- {
4235
+ wantLog (func () {
4188
4236
c , err := net .Dial ("tcp" , ts .Listener .Addr ().String ())
4189
4237
if err != nil {
4190
4238
t .Fatal (err )
@@ -4194,10 +4242,9 @@ func TestServerConnState(t *testing.T) {
4194
4242
}
4195
4243
c .Read (make ([]byte , 1 )) // block until server hangs up on us
4196
4244
c .Close ()
4197
- }
4245
+ }, StateNew , StateActive , StateClosed )
4198
4246
4199
- // New->Idle->Closed
4200
- {
4247
+ wantLog (func () {
4201
4248
c , err := net .Dial ("tcp" , ts .Listener .Addr ().String ())
4202
4249
if err != nil {
4203
4250
t .Fatal (err )
@@ -4213,47 +4260,7 @@ func TestServerConnState(t *testing.T) {
4213
4260
t .Fatal (err )
4214
4261
}
4215
4262
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.\n Got log:\n %s\n Want:\n %s\n " , logString (stateLog ), logString (want ))
4256
- mu .Unlock ()
4263
+ }, StateNew , StateActive , StateIdle , StateClosed )
4257
4264
}
4258
4265
4259
4266
func TestServerKeepAlivesEnabled (t * testing.T ) {
0 commit comments