in src/main/java/com/twitter/http2/HttpConnectionHandler.java [220:304]
public void readDataFrame(int streamId, boolean endStream, boolean endSegment, ByteBuf data) {
// HTTP/2 DATA frame processing requirements:
//
// If an endpoint receives a data frame for a Stream-ID which is not open
// and the endpoint has not sent a GOAWAY frame, it must issue a stream error
// with the error code INVALID_STREAM for the Stream-ID.
//
// If an endpoint receives multiple data frames for invalid Stream-IDs,
// it may close the connection.
//
// If an endpoint refuses a stream it must ignore any data frames for that stream.
//
// If an endpoint receives a data frame after the stream is half-closed (remote)
// or closed, it must respond with a stream error of type STREAM_CLOSED.
int deltaWindowSize = -1 * data.readableBytes();
int newConnectionWindowSize = httpConnection.updateReceiveWindowSize(
HTTP_CONNECTION_STREAM_ID, deltaWindowSize);
// Check if connection window size is reduced beyond allowable lower bound
if (newConnectionWindowSize < 0) {
issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
return;
}
// Send a WINDOW_UPDATE frame if less than half the connection window size remains
if (newConnectionWindowSize <= initialConnectionReceiveWindowSize / 2) {
int windowSizeIncrement = initialConnectionReceiveWindowSize - newConnectionWindowSize;
httpConnection.updateReceiveWindowSize(HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(
HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
context.writeAndFlush(frame);
}
// Check if we received a DATA frame for a stream which is half-closed (remote) or closed
if (httpConnection.isRemoteSideClosed(streamId)) {
if (streamId <= lastStreamId) {
issueStreamError(streamId, HttpErrorCode.STREAM_CLOSED);
} else if (!sentGoAwayFrame) {
issueStreamError(streamId, HttpErrorCode.PROTOCOL_ERROR);
}
return;
}
// Update receive window size
int newWindowSize = httpConnection.updateReceiveWindowSize(streamId, deltaWindowSize);
// Window size can become negative if we sent a SETTINGS frame that reduces the
// size of the transfer window after the peer has written data frames.
// The value is bounded by the length that SETTINGS frame decrease the window.
// This difference is stored for the connection when writing the SETTINGS frame
// and is cleared once we send a WINDOW_UPDATE frame.
if (newWindowSize < httpConnection.getReceiveWindowSizeLowerBound(streamId)) {
issueStreamError(streamId, HttpErrorCode.FLOW_CONTROL_ERROR);
return;
}
// Window size became negative due to sender writing frame before receiving SETTINGS
// Send data frames upstream in initialReceiveWindowSize chunks
if (newWindowSize < 0) {
while (data.readableBytes() > initialReceiveWindowSize) {
ByteBuf partialData = data.readBytes(initialReceiveWindowSize);
HttpDataFrame partialDataFrame = new DefaultHttpDataFrame(streamId, partialData);
context.fireChannelRead(partialDataFrame);
}
}
// Send a WINDOW_UPDATE frame if less than half the stream window size remains
// Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
if (handleStreamWindowUpdates && newWindowSize <= initialReceiveWindowSize / 2 && !endStream) {
int windowSizeIncrement = initialReceiveWindowSize - newWindowSize;
httpConnection.updateReceiveWindowSize(streamId, windowSizeIncrement);
ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(streamId, windowSizeIncrement);
context.writeAndFlush(frame);
}
// Close the remote side of the stream if this is the last frame
if (endStream) {
halfCloseStream(streamId, true, context.channel().newSucceededFuture());
}
HttpDataFrame httpDataFrame = new DefaultHttpDataFrame(streamId, data);
httpDataFrame.setLast(endStream);
context.fireChannelRead(httpDataFrame);
}