mirror of https://github.com/grpc/grpc-java.git
Merge pull request #177 from nmittler/irce
Proper buffer closure when receiving DATA with EOS
This commit is contained in:
commit
2c07e23911
|
|
@ -88,8 +88,12 @@ public abstract class AbstractClientStream<IdT> extends AbstractStream<IdT>
|
||||||
* responsible for properly closing streams when protocol errors occur.
|
* responsible for properly closing streams when protocol errors occur.
|
||||||
*
|
*
|
||||||
* @param errorStatus the error to report
|
* @param errorStatus the error to report
|
||||||
|
* @param stopDelivery if {@code true}, interrupts any further delivery of inbound messages that
|
||||||
|
* may already be queued up in the deframer. If {@code false}, the listener will be
|
||||||
|
* notified immediately after all currently completed messages in the deframer have been
|
||||||
|
* delivered to the application.
|
||||||
*/
|
*/
|
||||||
protected void inboundTransportError(Status errorStatus) {
|
protected void inboundTransportError(Status errorStatus, boolean stopDelivery) {
|
||||||
if (inboundPhase() == Phase.STATUS) {
|
if (inboundPhase() == Phase.STATUS) {
|
||||||
log.log(Level.INFO, "Received transport error on closed stream {0} {1}",
|
log.log(Level.INFO, "Received transport error on closed stream {0} {1}",
|
||||||
new Object[]{id(), errorStatus});
|
new Object[]{id(), errorStatus});
|
||||||
|
|
@ -97,7 +101,7 @@ public abstract class AbstractClientStream<IdT> extends AbstractStream<IdT>
|
||||||
}
|
}
|
||||||
// For transport errors we immediately report status to the application layer
|
// For transport errors we immediately report status to the application layer
|
||||||
// and do not wait for additional payloads.
|
// and do not wait for additional payloads.
|
||||||
transportReportStatus(errorStatus, false, new Metadata.Trailers());
|
transportReportStatus(errorStatus, stopDelivery, new Metadata.Trailers());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -121,22 +125,30 @@ public abstract class AbstractClientStream<IdT> extends AbstractStream<IdT>
|
||||||
* Processes the contents of a received data frame from the server.
|
* Processes the contents of a received data frame from the server.
|
||||||
*
|
*
|
||||||
* @param frame the received data frame. Its ownership is transferred to this method.
|
* @param frame the received data frame. Its ownership is transferred to this method.
|
||||||
|
* @param
|
||||||
*/
|
*/
|
||||||
protected void inboundDataReceived(Buffer frame) {
|
protected void inboundDataReceived(Buffer frame) {
|
||||||
Preconditions.checkNotNull(frame, "frame");
|
Preconditions.checkNotNull(frame, "frame");
|
||||||
if (inboundPhase() == Phase.STATUS) {
|
boolean needToCloseFrame = true;
|
||||||
frame.close();
|
try {
|
||||||
return;
|
if (inboundPhase() == Phase.STATUS) {
|
||||||
}
|
return;
|
||||||
if (inboundPhase() == Phase.HEADERS) {
|
}
|
||||||
// Have not received headers yet so error
|
if (inboundPhase() == Phase.HEADERS) {
|
||||||
inboundTransportError(Status.INTERNAL.withDescription("headers not received before payload"));
|
// Have not received headers yet so error
|
||||||
frame.close();
|
inboundTransportError(Status.INTERNAL
|
||||||
return;
|
.withDescription("headers not received before payload"), false);
|
||||||
}
|
return;
|
||||||
inboundPhase(Phase.MESSAGE);
|
}
|
||||||
|
inboundPhase(Phase.MESSAGE);
|
||||||
|
|
||||||
deframe(frame, false);
|
needToCloseFrame = false;
|
||||||
|
deframe(frame, false);
|
||||||
|
} finally {
|
||||||
|
if (needToCloseFrame) {
|
||||||
|
frame.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ public abstract class Http2ClientStream extends AbstractClientStream<Integer> {
|
||||||
Buffers.readAsString(frame, errorCharset));
|
Buffers.readAsString(frame, errorCharset));
|
||||||
frame.close();
|
frame.close();
|
||||||
if (transportError.getDescription().length() > 1000 || endOfStream) {
|
if (transportError.getDescription().length() > 1000 || endOfStream) {
|
||||||
inboundTransportError(transportError);
|
inboundTransportError(transportError, false);
|
||||||
if (!endOfStream) {
|
if (!endOfStream) {
|
||||||
// We have enough error detail so lets cancel.
|
// We have enough error detail so lets cancel.
|
||||||
sendCancel();
|
sendCancel();
|
||||||
|
|
@ -136,8 +136,7 @@ public abstract class Http2ClientStream extends AbstractClientStream<Integer> {
|
||||||
if (endOfStream) {
|
if (endOfStream) {
|
||||||
// This is a protocol violation as we expect to receive trailers.
|
// This is a protocol violation as we expect to receive trailers.
|
||||||
transportError = Status.INTERNAL.withDescription("Recevied EOS on DATA frame");
|
transportError = Status.INTERNAL.withDescription("Recevied EOS on DATA frame");
|
||||||
frame.close();
|
inboundTransportError(transportError, true);
|
||||||
inboundTransportError(transportError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +155,7 @@ public abstract class Http2ClientStream extends AbstractClientStream<Integer> {
|
||||||
transportError = checkContentType(trailers);
|
transportError = checkContentType(trailers);
|
||||||
}
|
}
|
||||||
if (transportError != null) {
|
if (transportError != null) {
|
||||||
inboundTransportError(transportError);
|
inboundTransportError(transportError, false);
|
||||||
} else {
|
} else {
|
||||||
Status status = statusFromTrailers(trailers);
|
Status status = statusFromTrailers(trailers);
|
||||||
stripTransportDetails(trailers);
|
stripTransportDetails(trailers);
|
||||||
|
|
|
||||||
|
|
@ -153,14 +153,23 @@ public class MessageDeframer implements Closeable {
|
||||||
* {@code endOfStream=true}.
|
* {@code endOfStream=true}.
|
||||||
*/
|
*/
|
||||||
public void deframe(Buffer data, boolean endOfStream) {
|
public void deframe(Buffer data, boolean endOfStream) {
|
||||||
checkNotClosed();
|
|
||||||
Preconditions.checkNotNull(data, "data");
|
Preconditions.checkNotNull(data, "data");
|
||||||
Preconditions.checkState(!this.endOfStream, "Past end of stream");
|
boolean needToCloseData = true;
|
||||||
unprocessed.addBuffer(data);
|
try {
|
||||||
|
checkNotClosed();
|
||||||
|
Preconditions.checkState(!this.endOfStream, "Past end of stream");
|
||||||
|
|
||||||
// Indicate that all of the data for this stream has been received.
|
needToCloseData = false;
|
||||||
this.endOfStream = endOfStream;
|
unprocessed.addBuffer(data);
|
||||||
deliver();
|
|
||||||
|
// Indicate that all of the data for this stream has been received.
|
||||||
|
this.endOfStream = endOfStream;
|
||||||
|
deliver();
|
||||||
|
} finally {
|
||||||
|
if (needToCloseData) {
|
||||||
|
data.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,25 @@ public class NettyClientStreamTest extends NettyStreamTestBase {
|
||||||
verify(listener).closed(eq(Status.CANCELLED), eq(trailers));
|
verify(listener).closed(eq(Status.CANCELLED), eq(trailers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dataFrameWithEosShouldDeframeAndThenFail() {
|
||||||
|
stream().id(1);
|
||||||
|
stream().request(1);
|
||||||
|
|
||||||
|
// Receive headers first so that it's a valid GRPC response.
|
||||||
|
stream().transportHeadersReceived(grpcResponseHeaders(), false);
|
||||||
|
|
||||||
|
// Receive a DATA frame with EOS set.
|
||||||
|
stream().transportDataReceived(simpleGrpcFrame(), true);
|
||||||
|
|
||||||
|
// Verify that the message was delivered.
|
||||||
|
verify(listener).messageRead(any(InputStream.class));
|
||||||
|
|
||||||
|
ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class);
|
||||||
|
verify(listener).closed(captor.capture(), any(Metadata.Trailers.class));
|
||||||
|
assertEquals(Status.Code.INTERNAL, captor.getValue().getCode());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractStream<Integer> createStream() {
|
protected AbstractStream<Integer> createStream() {
|
||||||
AbstractStream<Integer> stream = new NettyClientStream(listener, channel, handler);
|
AbstractStream<Integer> stream = new NettyClientStream(listener, channel, handler);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue