public void readDataFrame()

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);
    }