Skip to content

Commit 6b20036

Browse files
neildgopherbot
authored andcommitted
http2: add synchronous handler support to serverTester
It is often useful in server tests to orchestrate a sequence of actions that involve both a server connection and request handler. For example, we might want to have the request handler read from the request body at a precise point in the test. Add support for this to serverTester (used for most server tests). Pass a nil handler to serverTester, and it will provide synchronous access to the handler: call := st.nextHandlerCall() call.do(func(w http.ResponseWriter, r *http.Request) { // this executes in the handler goroutine }) Replace the existing handlerPuppet type, which provided a similar mechanism but only worked on a single call at a time. Change-Id: I023e032084f911ab4f9b803c393e4a55b12af87f Reviewed-on: https://go-review.googlesource.com/c/net/+/701002 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Nicholas Husin <[email protected]> Auto-Submit: Nicholas Husin <[email protected]> Reviewed-by: Nicholas Husin <[email protected]>
1 parent b0013c6 commit 6b20036

File tree

2 files changed

+71
-42
lines changed

2 files changed

+71
-42
lines changed

http2/http2_test.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,6 @@ func encodeHeaderNoImplicit(t testing.TB, headers ...string) []byte {
8383
return buf.Bytes()
8484
}
8585

86-
type puppetCommand struct {
87-
fn func(w http.ResponseWriter, r *http.Request)
88-
done chan<- bool
89-
}
90-
91-
type handlerPuppet struct {
92-
ch chan puppetCommand
93-
}
94-
95-
func newHandlerPuppet() *handlerPuppet {
96-
return &handlerPuppet{
97-
ch: make(chan puppetCommand),
98-
}
99-
}
100-
101-
func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
102-
for cmd := range p.ch {
103-
cmd.fn(w, r)
104-
cmd.done <- true
105-
}
106-
}
107-
108-
func (p *handlerPuppet) done() { close(p.ch) }
109-
func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
110-
done := make(chan bool)
111-
p.ch <- puppetCommand{fn, done}
112-
<-done
113-
}
114-
11586
func cleanDate(res *http.Response) {
11687
if d := res.Header["Date"]; len(d) == 1 {
11788
d[0] = "XXX"

http2/server_test.go

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ type serverTester struct {
7878
sc *serverConn
7979
testConnFramer
8080

81+
callsMu sync.Mutex
82+
calls []*serverHandlerCall
83+
8184
// If http2debug!=2, then we capture Frame debug logs that will be written
8285
// to t.Log after a test fails. The read and write logs use separate locks
8386
// and buffers so we don't accidentally introduce synchronization between
@@ -188,6 +191,10 @@ func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}
188191
h1server.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, &st.serverLogBuf), "", log.LstdFlags)
189192
}
190193

194+
if handler == nil {
195+
handler = serverTesterHandler{st}.ServeHTTP
196+
}
197+
191198
t.Cleanup(func() {
192199
st.Close()
193200
time.Sleep(goAwayTimeout) // give server time to shut down
@@ -226,6 +233,50 @@ func (c *netConnWithConnectionState) ConnectionState() tls.ConnectionState {
226233
return c.state
227234
}
228235

236+
type serverTesterHandler struct {
237+
st *serverTester
238+
}
239+
240+
func (h serverTesterHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
241+
call := &serverHandlerCall{
242+
w: w,
243+
req: req,
244+
ch: make(chan func()),
245+
}
246+
h.st.t.Cleanup(call.exit)
247+
h.st.callsMu.Lock()
248+
h.st.calls = append(h.st.calls, call)
249+
h.st.callsMu.Unlock()
250+
for f := range call.ch {
251+
f()
252+
}
253+
}
254+
255+
// serverHandlerCall is a call to the server handler's ServeHTTP method.
256+
type serverHandlerCall struct {
257+
w http.ResponseWriter
258+
req *http.Request
259+
closeOnce sync.Once
260+
ch chan func()
261+
}
262+
263+
// do executes f in the handler's goroutine.
264+
func (call *serverHandlerCall) do(f func(http.ResponseWriter, *http.Request)) {
265+
donec := make(chan struct{})
266+
call.ch <- func() {
267+
defer close(donec)
268+
f(call.w, call.req)
269+
}
270+
<-donec
271+
}
272+
273+
// exit causes the handler to return.
274+
func (call *serverHandlerCall) exit() {
275+
call.closeOnce.Do(func() {
276+
close(call.ch)
277+
})
278+
}
279+
229280
// newServerTesterWithRealConn creates a test server listening on a localhost port.
230281
// Mostly superseded by newServerTester, which creates a test server using a fake
231282
// net.Conn and synthetic time. This function is still around because some benchmarks
@@ -350,6 +401,19 @@ func (st *serverTester) addLogFilter(phrase string) {
350401
st.logFilter = append(st.logFilter, phrase)
351402
}
352403

404+
func (st *serverTester) nextHandlerCall() *serverHandlerCall {
405+
st.t.Helper()
406+
synctest.Wait()
407+
st.callsMu.Lock()
408+
defer st.callsMu.Unlock()
409+
if len(st.calls) == 0 {
410+
st.t.Fatal("expected server handler call, got none")
411+
}
412+
call := st.calls[0]
413+
st.calls = st.calls[1:]
414+
return call
415+
}
416+
353417
func (st *serverTester) stream(id uint32) *stream {
354418
ch := make(chan *stream, 1)
355419
st.sc.serveMsgCh <- func(int) {
@@ -1265,15 +1329,11 @@ func testServer_Handler_Sends_WindowUpdate(t testing.TB) {
12651329
//
12661330
// This also needs to be less than MAX_FRAME_SIZE.
12671331
const windowSize = 65535 * 2
1268-
puppet := newHandlerPuppet()
1269-
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
1270-
puppet.act(w, r)
1271-
}, func(s *Server) {
1332+
st := newServerTester(t, nil, func(s *Server) {
12721333
s.MaxUploadBufferPerConnection = windowSize
12731334
s.MaxUploadBufferPerStream = windowSize
12741335
})
12751336
defer st.Close()
1276-
defer puppet.done()
12771337

12781338
st.greet()
12791339
st.writeHeaders(HeadersFrameParam{
@@ -1282,13 +1342,14 @@ func testServer_Handler_Sends_WindowUpdate(t testing.TB) {
12821342
EndStream: false, // data coming
12831343
EndHeaders: true,
12841344
})
1345+
call := st.nextHandlerCall()
12851346

12861347
// Write less than half the max window of data and consume it.
12871348
// The server doesn't return flow control yet, buffering the 1024 bytes to
12881349
// combine with a future update.
12891350
data := make([]byte, windowSize)
12901351
st.writeData(1, false, data[:1024])
1291-
puppet.do(readBodyHandler(t, string(data[:1024])))
1352+
call.do(readBodyHandler(t, string(data[:1024])))
12921353

12931354
// Write up to the window limit.
12941355
// The server returns the buffered credit.
@@ -1297,7 +1358,7 @@ func testServer_Handler_Sends_WindowUpdate(t testing.TB) {
12971358
st.wantWindowUpdate(1, 1024)
12981359

12991360
// The handler consumes the data and the server returns credit.
1300-
puppet.do(readBodyHandler(t, string(data[1024:])))
1361+
call.do(readBodyHandler(t, string(data[1024:])))
13011362
st.wantWindowUpdate(0, windowSize-1024)
13021363
st.wantWindowUpdate(1, windowSize-1024)
13031364
}
@@ -1309,15 +1370,11 @@ func TestServer_Handler_Sends_WindowUpdate_Padding(t *testing.T) {
13091370
}
13101371
func testServer_Handler_Sends_WindowUpdate_Padding(t testing.TB) {
13111372
const windowSize = 65535 * 2
1312-
puppet := newHandlerPuppet()
1313-
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
1314-
puppet.act(w, r)
1315-
}, func(s *Server) {
1373+
st := newServerTester(t, nil, func(s *Server) {
13161374
s.MaxUploadBufferPerConnection = windowSize
13171375
s.MaxUploadBufferPerStream = windowSize
13181376
})
13191377
defer st.Close()
1320-
defer puppet.done()
13211378

13221379
st.greet()
13231380
st.writeHeaders(HeadersFrameParam{
@@ -1326,6 +1383,7 @@ func testServer_Handler_Sends_WindowUpdate_Padding(t testing.TB) {
13261383
EndStream: false,
13271384
EndHeaders: true,
13281385
})
1386+
call := st.nextHandlerCall()
13291387

13301388
// Write half a window of data, with some padding.
13311389
// The server doesn't return the padding yet, buffering the 5 bytes to combine
@@ -1337,7 +1395,7 @@ func testServer_Handler_Sends_WindowUpdate_Padding(t testing.TB) {
13371395
// The handler consumes the body.
13381396
// The server returns flow control for the body and padding
13391397
// (4 bytes of padding + 1 byte of length).
1340-
puppet.do(readBodyHandler(t, string(data)))
1398+
call.do(readBodyHandler(t, string(data)))
13411399
st.wantWindowUpdate(0, uint32(len(data)+1+len(pad)))
13421400
st.wantWindowUpdate(1, uint32(len(data)+1+len(pad)))
13431401
}

0 commit comments

Comments
 (0)