Skip to content

Commit 03c24c2

Browse files
committed
http2: use synthetic time in server tests
Change newServerTester to return a server using fake time and a fake net.Conn. Change-Id: I9d5db0cbe75696aed6d99ff1cd2369c2dea426c3 Reviewed-on: https://go-review.googlesource.com/c/net/+/586247 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
1 parent 022530c commit 03c24c2

File tree

7 files changed

+318
-156
lines changed

7 files changed

+318
-156
lines changed

http2/http2.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package http2 // import "golang.org/x/net/http2"
1717

1818
import (
1919
"bufio"
20+
"context"
2021
"crypto/tls"
2122
"fmt"
2223
"io"
@@ -26,6 +27,7 @@ import (
2627
"strconv"
2728
"strings"
2829
"sync"
30+
"time"
2931

3032
"golang.org/x/net/http/httpguts"
3133
)
@@ -377,3 +379,14 @@ func validPseudoPath(v string) bool {
377379
// makes that struct also non-comparable, and generally doesn't add
378380
// any size (as long as it's first).
379381
type incomparable [0]func()
382+
383+
// synctestGroupInterface is the methods of synctestGroup used by Server and Transport.
384+
// It's defined as an interface here to let us keep synctestGroup entirely test-only
385+
// and not a part of non-test builds.
386+
type synctestGroupInterface interface {
387+
Join()
388+
Now() time.Time
389+
NewTimer(d time.Duration) timer
390+
AfterFunc(d time.Duration, f func()) timer
391+
ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
392+
}

http2/server.go

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,39 @@ type Server struct {
154154
// so that we don't embed a Mutex in this struct, which will make the
155155
// struct non-copyable, which might break some callers.
156156
state *serverInternalState
157+
158+
// Synchronization group used for testing.
159+
// Outside of tests, this is nil.
160+
group synctestGroupInterface
161+
}
162+
163+
func (s *Server) markNewGoroutine() {
164+
if s.group != nil {
165+
s.group.Join()
166+
}
167+
}
168+
169+
func (s *Server) now() time.Time {
170+
if s.group != nil {
171+
return s.group.Now()
172+
}
173+
return time.Now()
174+
}
175+
176+
// newTimer creates a new time.Timer, or a synthetic timer in tests.
177+
func (s *Server) newTimer(d time.Duration) timer {
178+
if s.group != nil {
179+
return s.group.NewTimer(d)
180+
}
181+
return timeTimer{time.NewTimer(d)}
182+
}
183+
184+
// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
185+
func (s *Server) afterFunc(d time.Duration, f func()) timer {
186+
if s.group != nil {
187+
return s.group.AfterFunc(d, f)
188+
}
189+
return timeTimer{time.AfterFunc(d, f)}
157190
}
158191

159192
func (s *Server) initialConnRecvWindowSize() int32 {
@@ -400,6 +433,10 @@ func (o *ServeConnOpts) handler() http.Handler {
400433
//
401434
// The opts parameter is optional. If nil, default values are used.
402435
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
436+
s.serveConn(c, opts, nil)
437+
}
438+
439+
func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverConn)) {
403440
baseCtx, cancel := serverConnBaseContext(c, opts)
404441
defer cancel()
405442

@@ -426,6 +463,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
426463
pushEnabled: true,
427464
sawClientPreface: opts.SawClientPreface,
428465
}
466+
if newf != nil {
467+
newf(sc)
468+
}
429469

430470
s.state.registerConn(sc)
431471
defer s.state.unregisterConn(sc)
@@ -599,8 +639,8 @@ type serverConn struct {
599639
inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
600640
needToSendGoAway bool // we need to schedule a GOAWAY frame write
601641
goAwayCode ErrCode
602-
shutdownTimer *time.Timer // nil until used
603-
idleTimer *time.Timer // nil if unused
642+
shutdownTimer timer // nil until used
643+
idleTimer timer // nil if unused
604644

605645
// Owned by the writeFrameAsync goroutine:
606646
headerWriteBuf bytes.Buffer
@@ -649,12 +689,12 @@ type stream struct {
649689
flow outflow // limits writing from Handler to client
650690
inflow inflow // what the client is allowed to POST/etc to us
651691
state streamState
652-
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
653-
gotTrailerHeader bool // HEADER frame for trailers was seen
654-
wroteHeaders bool // whether we wrote headers (not status 100)
655-
readDeadline *time.Timer // nil if unused
656-
writeDeadline *time.Timer // nil if unused
657-
closeErr error // set before cw is closed
692+
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
693+
gotTrailerHeader bool // HEADER frame for trailers was seen
694+
wroteHeaders bool // whether we wrote headers (not status 100)
695+
readDeadline timer // nil if unused
696+
writeDeadline timer // nil if unused
697+
closeErr error // set before cw is closed
658698

659699
trailer http.Header // accumulated trailers
660700
reqTrailer http.Header // handler's Request.Trailer
@@ -811,6 +851,7 @@ type readFrameResult struct {
811851
// consumer is done with the frame.
812852
// It's run on its own goroutine.
813853
func (sc *serverConn) readFrames() {
854+
sc.srv.markNewGoroutine()
814855
gate := make(chan struct{})
815856
gateDone := func() { gate <- struct{}{} }
816857
for {
@@ -843,6 +884,7 @@ type frameWriteResult struct {
843884
// At most one goroutine can be running writeFrameAsync at a time per
844885
// serverConn.
845886
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) {
887+
sc.srv.markNewGoroutine()
846888
var err error
847889
if wd == nil {
848890
err = wr.write.writeFrame(sc)
@@ -922,13 +964,13 @@ func (sc *serverConn) serve() {
922964
sc.setConnState(http.StateIdle)
923965

924966
if sc.srv.IdleTimeout > 0 {
925-
sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
967+
sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
926968
defer sc.idleTimer.Stop()
927969
}
928970

929971
go sc.readFrames() // closed by defer sc.conn.Close above
930972

931-
settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer)
973+
settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer)
932974
defer settingsTimer.Stop()
933975

934976
loopNum := 0
@@ -1057,10 +1099,10 @@ func (sc *serverConn) readPreface() error {
10571099
errc <- nil
10581100
}
10591101
}()
1060-
timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server?
1102+
timer := sc.srv.newTimer(prefaceTimeout) // TODO: configurable on *Server?
10611103
defer timer.Stop()
10621104
select {
1063-
case <-timer.C:
1105+
case <-timer.C():
10641106
return errPrefaceTimeout
10651107
case err := <-errc:
10661108
if err == nil {
@@ -1425,7 +1467,7 @@ func (sc *serverConn) goAway(code ErrCode) {
14251467

14261468
func (sc *serverConn) shutDownIn(d time.Duration) {
14271469
sc.serveG.check()
1428-
sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer)
1470+
sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer)
14291471
}
14301472

14311473
func (sc *serverConn) resetStream(se StreamError) {
@@ -2022,7 +2064,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
20222064
// (in Go 1.8), though. That's a more sane option anyway.
20232065
if sc.hs.ReadTimeout > 0 {
20242066
sc.conn.SetReadDeadline(time.Time{})
2025-
st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
2067+
st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
20262068
}
20272069

20282070
return sc.scheduleHandler(id, rw, req, handler)
@@ -2120,7 +2162,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
21202162
st.flow.add(sc.initialStreamSendWindowSize)
21212163
st.inflow.init(sc.srv.initialStreamRecvWindowSize())
21222164
if sc.hs.WriteTimeout > 0 {
2123-
st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
2165+
st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
21242166
}
21252167

21262168
sc.streams[id] = st
@@ -2344,6 +2386,7 @@ func (sc *serverConn) handlerDone() {
23442386

23452387
// Run on its own goroutine.
23462388
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
2389+
sc.srv.markNewGoroutine()
23472390
defer sc.sendServeMsg(handlerDoneMsg)
23482391
didPanic := true
23492392
defer func() {
@@ -2640,7 +2683,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
26402683
var date string
26412684
if _, ok := rws.snapHeader["Date"]; !ok {
26422685
// TODO(bradfitz): be faster here, like net/http? measure.
2643-
date = time.Now().UTC().Format(http.TimeFormat)
2686+
date = rws.conn.srv.now().UTC().Format(http.TimeFormat)
26442687
}
26452688

26462689
for _, v := range rws.snapHeader["Trailer"] {
@@ -2762,7 +2805,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
27622805

27632806
func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
27642807
st := w.rws.stream
2765-
if !deadline.IsZero() && deadline.Before(time.Now()) {
2808+
if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) {
27662809
// If we're setting a deadline in the past, reset the stream immediately
27672810
// so writes after SetWriteDeadline returns will fail.
27682811
st.onReadTimeout()
@@ -2778,17 +2821,17 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
27782821
if deadline.IsZero() {
27792822
st.readDeadline = nil
27802823
} else if st.readDeadline == nil {
2781-
st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
2824+
st.readDeadline = sc.srv.afterFunc(deadline.Sub(w.rws.conn.srv.now()), st.onReadTimeout)
27822825
} else {
2783-
st.readDeadline.Reset(deadline.Sub(time.Now()))
2826+
st.readDeadline.Reset(deadline.Sub(w.rws.conn.srv.now()))
27842827
}
27852828
})
27862829
return nil
27872830
}
27882831

27892832
func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
27902833
st := w.rws.stream
2791-
if !deadline.IsZero() && deadline.Before(time.Now()) {
2834+
if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) {
27922835
// If we're setting a deadline in the past, reset the stream immediately
27932836
// so writes after SetWriteDeadline returns will fail.
27942837
st.onWriteTimeout()
@@ -2804,9 +2847,9 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
28042847
if deadline.IsZero() {
28052848
st.writeDeadline = nil
28062849
} else if st.writeDeadline == nil {
2807-
st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
2850+
st.writeDeadline = sc.srv.afterFunc(deadline.Sub(w.rws.conn.srv.now()), st.onWriteTimeout)
28082851
} else {
2809-
st.writeDeadline.Reset(deadline.Sub(time.Now()))
2852+
st.writeDeadline.Reset(deadline.Sub(w.rws.conn.srv.now()))
28102853
}
28112854
})
28122855
return nil

http2/server_push_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func TestServer_Push_Success(t *testing.T) {
105105
errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
106106
}
107107
})
108-
stURL = st.ts.URL
108+
stURL = "https://" + st.authority()
109109

110110
// Send one request, which should push two responses.
111111
st.greet()
@@ -169,7 +169,7 @@ func TestServer_Push_Success(t *testing.T) {
169169
return checkPushPromise(f, 2, [][2]string{
170170
{":method", "GET"},
171171
{":scheme", "https"},
172-
{":authority", st.ts.Listener.Addr().String()},
172+
{":authority", st.authority()},
173173
{":path", "/pushed?get"},
174174
{"user-agent", userAgent},
175175
})
@@ -178,7 +178,7 @@ func TestServer_Push_Success(t *testing.T) {
178178
return checkPushPromise(f, 4, [][2]string{
179179
{":method", "HEAD"},
180180
{":scheme", "https"},
181-
{":authority", st.ts.Listener.Addr().String()},
181+
{":authority", st.authority()},
182182
{":path", "/pushed?head"},
183183
{"cookie", cookie},
184184
{"user-agent", userAgent},

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy