-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
client: fix ClientStream.Header() behavior #6557
Changes from 3 commits
39f4230
340a371
0e6d109
11a7cd2
599d7c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1505,30 +1505,25 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
return | ||
} | ||
|
||
isHeader := false | ||
|
||
// If headerChan hasn't been closed yet | ||
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { | ||
s.headerValid = true | ||
if !endStream { | ||
// HEADERS frame block carries a Response-Headers. | ||
isHeader = true | ||
if !endStream { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional (use some/all/none): mention this is headers flow, and that for trailers flow, header chan gets closed in closeStream() which writes status before unblocking the header chan, letting client stream read the status after it's block on header chan (in csAttempt). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment |
||
// If headerChan hasn't been closed yet (expected, given we checked it | ||
// above, but something else could have potentially closed the whole | ||
// stream). | ||
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { | ||
s.headerValid = true | ||
// These values can be set without any synchronization because | ||
// stream goroutine will read it only after seeing a closed | ||
// headerChan which we'll close after setting this. | ||
s.recvCompress = recvCompress | ||
if len(mdata) > 0 { | ||
s.header = mdata | ||
} | ||
} else { | ||
// HEADERS frame block carries a Trailers-Only. | ||
s.noHeaders = true | ||
close(s.headerChan) | ||
} | ||
close(s.headerChan) | ||
} | ||
|
||
for _, sh := range t.statsHandlers { | ||
if isHeader { | ||
if !endStream { | ||
inHeader := &stats.InHeader{ | ||
Client: true, | ||
WireLength: int(frame.Header().Length), | ||
|
@@ -1554,9 +1549,10 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
statusGen = status.New(rawStatusCode, grpcMessage) | ||
} | ||
|
||
// if client received END_STREAM from server while stream was still active, send RST_STREAM | ||
rst := s.getState() == streamActive | ||
t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, statusGen, mdata, true) | ||
// If client received END_STREAM from server while stream was still active, | ||
// send RST_STREAM. | ||
rstStream := s.getState() == streamActive | ||
t.closeStream(s, io.EOF, rstStream, http2.ErrCodeNo, statusGen, mdata, true) | ||
} | ||
|
||
// readServerPreface reads and handles the initial settings frame from the | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -789,23 +789,23 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func()) | |
|
||
func (cs *clientStream) Header() (metadata.MD, error) { | ||
var m metadata.MD | ||
noHeader := false | ||
err := cs.withRetry(func(a *csAttempt) error { | ||
var err error | ||
m, err = a.s.Header() | ||
if err == transport.ErrNoHeaders { | ||
noHeader = true | ||
return nil | ||
} | ||
return toRPCErr(err) | ||
}, cs.commitAttemptLocked) | ||
|
||
if m == nil && err == nil { | ||
// The stream ended with success. Finish the clientStream. | ||
err = io.EOF | ||
} | ||
|
||
if err != nil { | ||
cs.finish(err) | ||
return nil, err | ||
} | ||
|
||
if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged && !noHeader { | ||
if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged && m != nil { | ||
// Only log if binary log is on and header has not been logged, and | ||
// there is actually headers to log. | ||
logEntry := &binarylog.ServerHeader{ | ||
|
@@ -821,6 +821,7 @@ func (cs *clientStream) Header() (metadata.MD, error) { | |
binlog.Log(cs.ctx, logEntry) | ||
} | ||
} | ||
|
||
return m, nil | ||
} | ||
|
||
|
@@ -929,24 +930,6 @@ func (cs *clientStream) RecvMsg(m any) error { | |
if err != nil || !cs.desc.ServerStreams { | ||
// err != nil or non-server-streaming indicates end of stream. | ||
cs.finish(err) | ||
|
||
if len(cs.binlogs) != 0 { | ||
// finish will not log Trailer. Log Trailer here. | ||
logEntry := &binarylog.ServerTrailer{ | ||
OnClientSide: true, | ||
Trailer: cs.Trailer(), | ||
Err: err, | ||
} | ||
if logEntry.Err == io.EOF { | ||
logEntry.Err = nil | ||
} | ||
if peer, ok := peer.FromContext(cs.Context()); ok { | ||
logEntry.PeerAddr = peer.Addr | ||
} | ||
for _, binlog := range cs.binlogs { | ||
binlog.Log(cs.ctx, logEntry) | ||
} | ||
} | ||
} | ||
return err | ||
} | ||
|
@@ -1002,18 +985,33 @@ func (cs *clientStream) finish(err error) { | |
} | ||
} | ||
} | ||
|
||
cs.mu.Unlock() | ||
// For binary logging. only log cancel in finish (could be caused by RPC ctx | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now it logs cancel and server trailers :). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I think the logic here is a bit wrong, since the server could return a |
||
// canceled or ClientConn closed). Trailer will be logged in RecvMsg. | ||
// canceled or ClientConn closed). | ||
// | ||
// Only one of cancel or trailer needs to be logged. In the cases where | ||
// users don't call RecvMsg, users must have already canceled the RPC. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: delete this comment, no longer applies since we don't log both events in different places: "In the cases where users don't call RecvMsg, users must have already canceled the RPC." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
if len(cs.binlogs) != 0 && status.Code(err) == codes.Canceled { | ||
c := &binarylog.Cancel{ | ||
OnClientSide: true, | ||
} | ||
for _, binlog := range cs.binlogs { | ||
binlog.Log(cs.ctx, c) | ||
if len(cs.binlogs) != 0 { | ||
if status.Code(err) == codes.Canceled { | ||
c := &binarylog.Cancel{ | ||
OnClientSide: true, | ||
} | ||
for _, binlog := range cs.binlogs { | ||
binlog.Log(cs.ctx, c) | ||
} | ||
} else { | ||
logEntry := &binarylog.ServerTrailer{ | ||
OnClientSide: true, | ||
Trailer: cs.Trailer(), | ||
Err: err, | ||
} | ||
if peer, ok := peer.FromContext(cs.Context()); ok { | ||
logEntry.PeerAddr = peer.Addr | ||
} | ||
for _, binlog := range cs.binlogs { | ||
binlog.Log(cs.ctx, logEntry) | ||
} | ||
} | ||
} | ||
if err == nil { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6339,12 +6339,11 @@ func (s) TestGlobalBinaryLoggingOptions(t *testing.T) { | |
return &testpb.SimpleResponse{}, nil | ||
}, | ||
FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error { | ||
for { | ||
_, err := stream.Recv() | ||
if err == io.EOF { | ||
return nil | ||
} | ||
_, err := stream.Recv() | ||
if err == io.EOF { | ||
return nil | ||
} | ||
return status.Errorf(codes.Unknown, "expected client to CloseSend") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: s/CloseSend/call CloseSend(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
}, | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: keep this comment?