@@ -52,22 +52,19 @@ class NettyServerStream extends AbstractServerStream {
5252 private final WriteQueue writeQueue ;
5353 private final Attributes attributes ;
5454 private final String authority ;
55- private final TransportTracer transportTracer ;
5655 private final int streamId ;
5756
5857 public NettyServerStream (
5958 Channel channel ,
6059 TransportState state ,
6160 Attributes transportAttrs ,
6261 String authority ,
63- StatsTraceContext statsTraceCtx ,
64- TransportTracer transportTracer ) {
62+ StatsTraceContext statsTraceCtx ) {
6563 super (new NettyWritableBufferAllocator (channel .alloc ()), statsTraceCtx );
6664 this .state = checkNotNull (state , "transportState" );
6765 this .writeQueue = state .handler .getWriteQueue ();
6866 this .attributes = checkNotNull (transportAttrs );
6967 this .authority = authority ;
70- this .transportTracer = checkNotNull (transportTracer , "transportTracer" );
7168 // Read the id early to avoid reading transportState later.
7269 this .streamId = transportState ().id ();
7370 }
@@ -96,48 +93,37 @@ private class Sink implements AbstractServerStream.Sink {
9693 @ Override
9794 public void writeHeaders (Metadata headers , boolean flush ) {
9895 try (TaskCloseable ignore = PerfMark .traceTask ("NettyServerStream$Sink.writeHeaders" )) {
99- writeQueue . enqueue (
100- SendResponseHeadersCommand . createHeaders (
101- transportState (),
102- Utils . convertServerHeaders ( headers )),
103- flush );
96+ Http2Headers http2headers = Utils . convertServerHeaders ( headers );
97+ SendResponseHeadersCommand headersCommand =
98+ SendResponseHeadersCommand . createHeaders ( transportState (), http2headers );
99+ writeQueue . enqueue ( headersCommand , flush )
100+ . addListener (( ChannelFutureListener ) transportState ():: handleWriteFutureFailures );
104101 }
105102 }
106103
107- private void writeFrameInternal (WritableBuffer frame , boolean flush , final int numMessages ) {
108- Preconditions .checkArgument (numMessages >= 0 );
109- ByteBuf bytebuf = ((NettyWritableBuffer ) frame ).bytebuf ().touch ();
110- final int numBytes = bytebuf .readableBytes ();
111- // Add the bytes to outbound flow control.
112- onSendingBytes (numBytes );
113- writeQueue .enqueue (new SendGrpcFrameCommand (transportState (), bytebuf , false ), flush )
114- .addListener (new ChannelFutureListener () {
115- @ Override
116- public void operationComplete (ChannelFuture future ) throws Exception {
117- // Remove the bytes from outbound flow control, optionally notifying
118- // the client that they can send more bytes.
119- transportState ().onSentBytes (numBytes );
120- if (future .isSuccess ()) {
121- transportTracer .reportMessageSent (numMessages );
122- }
123- }
124- });
125- }
126-
127104 @ Override
128105 public void writeFrame (WritableBuffer frame , boolean flush , final int numMessages ) {
129106 try (TaskCloseable ignore = PerfMark .traceTask ("NettyServerStream$Sink.writeFrame" )) {
130- writeFrameInternal (frame , flush , numMessages );
107+ Preconditions .checkArgument (numMessages >= 0 );
108+ ByteBuf bytebuf = ((NettyWritableBuffer ) frame ).bytebuf ().touch ();
109+ final int numBytes = bytebuf .readableBytes ();
110+ // Add the bytes to outbound flow control.
111+ onSendingBytes (numBytes );
112+ ChannelFutureListener failureListener =
113+ future -> transportState ().onWriteFrameData (future , numMessages , numBytes );
114+ writeQueue .enqueue (new SendGrpcFrameCommand (transportState (), bytebuf , false ), flush )
115+ .addListener (failureListener );
131116 }
132117 }
133118
134119 @ Override
135120 public void writeTrailers (Metadata trailers , boolean headersSent , Status status ) {
136121 try (TaskCloseable ignore = PerfMark .traceTask ("NettyServerStream$Sink.writeTrailers" )) {
137122 Http2Headers http2Trailers = Utils .convertTrailers (trailers , headersSent );
138- writeQueue .enqueue (
139- SendResponseHeadersCommand .createTrailers (transportState (), http2Trailers , status ),
140- true );
123+ SendResponseHeadersCommand trailersCommand =
124+ SendResponseHeadersCommand .createTrailers (transportState (), http2Trailers , status );
125+ writeQueue .enqueue (trailersCommand , true )
126+ .addListener ((ChannelFutureListener ) transportState ()::handleWriteFutureFailures );
141127 }
142128 }
143129
@@ -206,6 +192,39 @@ public void deframeFailed(Throwable cause) {
206192 handler .getWriteQueue ().enqueue (new CancelServerStreamCommand (this , status ), true );
207193 }
208194
195+ private void onWriteFrameData (ChannelFuture future , int numMessages , int numBytes ) {
196+ // Remove the bytes from outbound flow control, optionally notifying
197+ // the client that they can send more bytes.
198+ if (future .isSuccess ()) {
199+ onSentBytes (numBytes );
200+ getTransportTracer ().reportMessageSent (numMessages );
201+ } else {
202+ handleWriteFutureFailures (future );
203+ }
204+ }
205+
206+ private void handleWriteFutureFailures (ChannelFuture future ) {
207+ // isStreamDeallocated() check protects from spamming stream resets by scheduling multiple
208+ // CancelServerStreamCommand commands.
209+ if (future .isSuccess () || isStreamDeallocated ()) {
210+ return ;
211+ }
212+
213+ // Future failed, fail RPC.
214+ // Normally we don't need to do anything on frame write failures because the cause of
215+ // the failed future would be an IO error that closed the stream.
216+ // However, we still need handle any unexpected failures raised in Netty.
217+ http2ProcessingFailed (Utils .statusFromThrowable (future .cause ()));
218+ }
219+
220+ /**
221+ * Called to process a failure in HTTP/2 processing.
222+ */
223+ protected void http2ProcessingFailed (Status status ) {
224+ transportReportStatus (status );
225+ handler .getWriteQueue ().enqueue (new CancelServerStreamCommand (this , status ), true );
226+ }
227+
209228 void inboundDataReceived (ByteBuf frame , boolean endOfStream ) {
210229 super .inboundDataReceived (new NettyReadableBuffer (frame .retain ()), endOfStream );
211230 }
0 commit comments