transport: fix handling of InTapHandle's returned context (#1461)

This commit is contained in:
dfawley
2017-08-25 13:18:45 -07:00
committed by GitHub
parent 8336807293
commit 0b4b292f3a
2 changed files with 75 additions and 34 deletions

View File

@ -4477,14 +4477,14 @@ func (ss *stubServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallSer
}
// Start starts the server and creates a client connected to it.
func (ss *stubServer) Start() error {
func (ss *stubServer) Start(sopts []grpc.ServerOption) error {
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
return fmt.Errorf(`net.Listen("tcp", "localhost:0") = %v`, err)
}
ss.cleanups = append(ss.cleanups, func() { lis.Close() })
s := grpc.NewServer()
s := grpc.NewServer(sopts...)
testpb.RegisterTestServiceServer(s, ss)
go s.Serve(lis)
ss.cleanups = append(ss.cleanups, s.Stop)
@ -4517,7 +4517,7 @@ func TestUnaryProxyDoesNotForwardMetadata(t *testing.T) {
return &testpb.Empty{}, nil
},
}
if err := endpoint.Start(); err != nil {
if err := endpoint.Start(nil); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer endpoint.Stop()
@ -4532,7 +4532,7 @@ func TestUnaryProxyDoesNotForwardMetadata(t *testing.T) {
return endpoint.client.EmptyCall(ctx, in)
},
}
if err := proxy.Start(); err != nil {
if err := proxy.Start(nil); err != nil {
t.Fatalf("Error starting proxy server: %v", err)
}
defer proxy.Stop()
@ -4580,7 +4580,7 @@ func TestStreamingProxyDoesNotForwardMetadata(t *testing.T) {
return nil
},
}
if err := endpoint.Start(); err != nil {
if err := endpoint.Start(nil); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer endpoint.Stop()
@ -4596,7 +4596,7 @@ func TestStreamingProxyDoesNotForwardMetadata(t *testing.T) {
return doFDC(ctx, endpoint.client)
},
}
if err := proxy.Start(); err != nil {
if err := proxy.Start(nil); err != nil {
t.Fatalf("Error starting proxy server: %v", err)
}
defer proxy.Stop()
@ -4642,7 +4642,7 @@ func TestStatsTagsAndTrace(t *testing.T) {
return &testpb.Empty{}, nil
},
}
if err := endpoint.Start(); err != nil {
if err := endpoint.Start(nil); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer endpoint.Stop()
@ -4672,6 +4672,41 @@ func TestStatsTagsAndTrace(t *testing.T) {
}
}
func TestTapTimeout(t *testing.T) {
sopts := []grpc.ServerOption{
grpc.InTapHandle(func(ctx context.Context, _ *tap.Info) (context.Context, error) {
c, cancel := context.WithCancel(ctx)
// Call cancel instead of setting a deadline so we can detect which error
// occurred -- this cancellation (desired) or the client's deadline
// expired (indicating this cancellation did not affect the RPC).
time.AfterFunc(10*time.Millisecond, cancel)
return c, nil
}),
}
ss := &stubServer{
emptyCall: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
<-ctx.Done()
return &testpb.Empty{}, nil
},
}
if err := ss.Start(sopts); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer ss.Stop()
// This was known to be flaky; test several times.
for i := 0; i < 10; i++ {
// Set our own deadline in case the server hangs.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
res, err := ss.client.EmptyCall(ctx, &testpb.Empty{})
cancel()
if s, ok := status.FromError(err); !ok || s.Code() != codes.Canceled {
t.Fatalf("ss.client.EmptyCall(context.Background(), _) = %v, %v; want nil, <status with Code()=Canceled>", res, err)
}
}
}
type windowSizeConfig struct {
serverStream int32
serverConn int32

View File

@ -230,29 +230,32 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
// operateHeader takes action on the decoded headers.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (close bool) {
buf := newRecvBuffer()
s := &Stream{
id: frame.Header().StreamID,
st: t,
buf: buf,
fc: &inFlow{limit: uint32(t.initialWindowSize)},
}
streamID := frame.Header().StreamID
var state decodeState
for _, hf := range frame.Fields {
if err := state.processHeaderField(hf); err != nil {
if se, ok := err.(StreamError); ok {
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
t.controlBuf.put(&resetStream{streamID, statusCodeConvTab[se.Code]})
}
return
}
}
buf := newRecvBuffer()
s := &Stream{
id: streamID,
st: t,
buf: buf,
fc: &inFlow{limit: uint32(t.initialWindowSize)},
recvCompress: state.encoding,
method: state.method,
}
if frame.StreamEnded() {
// s is just created by the caller. No lock needed.
s.state = streamReadDone
}
s.recvCompress = state.encoding
if state.timeoutSet {
s.ctx, s.cancel = context.WithTimeout(t.ctx, state.timeout)
} else {
@ -280,17 +283,6 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
if state.statsTrace != nil {
s.ctx = stats.SetIncomingTrace(s.ctx, state.statsTrace)
}
s.trReader = &transportReader{
reader: &recvBufferReader{
ctx: s.ctx,
recv: s.buf,
},
windowHandler: func(n int) {
t.updateWindow(s, uint32(n))
},
}
s.recvCompress = state.encoding
s.method = state.method
if t.inTapHandle != nil {
var err error
info := &tap.Info{
@ -310,18 +302,18 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
}
if uint32(len(t.activeStreams)) >= t.maxStreams {
t.mu.Unlock()
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
t.controlBuf.put(&resetStream{streamID, http2.ErrCodeRefusedStream})
return
}
if s.id%2 != 1 || s.id <= t.maxStreamID {
if streamID%2 != 1 || streamID <= t.maxStreamID {
t.mu.Unlock()
// illegal gRPC stream id.
errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", s.id)
errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
return true
}
t.maxStreamID = s.id
t.maxStreamID = streamID
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
t.activeStreams[s.id] = s
t.activeStreams[streamID] = s
if len(t.activeStreams) == 1 {
t.idle = time.Time{}
}
@ -341,6 +333,15 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
}
t.stats.HandleRPC(s.ctx, inHeader)
}
s.trReader = &transportReader{
reader: &recvBufferReader{
ctx: s.ctx,
recv: s.buf,
},
windowHandler: func(n int) {
t.updateWindow(s, uint32(n))
},
}
handle(s)
return
}
@ -774,8 +775,13 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
headersSent = true
}
if _, err := wait(s.ctx, nil, nil, t.shutdownChan, t.writableChan); err != nil {
return err
// Always write a status regardless of context cancellation unless the stream
// is terminated (e.g. by a RST_STREAM, GOAWAY, or transport error). The
// server's application code is already done so it is fine to ignore s.ctx.
select {
case <-t.shutdownChan:
return ErrConnClosing
case <-t.writableChan:
}
t.hBuf.Reset()
if !headersSent {