All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.firefly.codec.http2.decode.DataBodyParser Maven / Gradle / Ivy

There is a newer version: 4.0.3.2
Show newest version
package com.firefly.codec.http2.decode;

import java.nio.ByteBuffer;

import com.firefly.codec.http2.frame.DataFrame;
import com.firefly.codec.http2.frame.ErrorCode;
import com.firefly.utils.io.BufferUtils;

public class DataBodyParser extends BodyParser {
	private State state = State.PREPARE;
	private int padding;
	private int paddingLength;
	private int length;

	public DataBodyParser(HeaderParser headerParser, Parser.Listener listener) {
		super(headerParser, listener);
	}

	private void reset() {
		state = State.PREPARE;
		padding = 0;
		paddingLength = 0;
		length = 0;
	}

	@Override
	protected void emptyBody(ByteBuffer buffer) {
		if (isPadding())
			connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_data_frame");
		else
			onData(BufferUtils.EMPTY_BUFFER, false, 0);
	}

	@Override
	public boolean parse(ByteBuffer buffer) {
		boolean loop = false;
		while (buffer.hasRemaining() || loop) {
			switch (state) {
			case PREPARE: {
				// SPEC: wrong streamId is treated as connection error.
				if (getStreamId() == 0)
					return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_data_frame");

				length = getBodyLength();
				state = isPadding() ? State.PADDING_LENGTH : State.DATA;
				break;
			}
			case PADDING_LENGTH: {
				padding = 1; // We have seen this byte.
				paddingLength = buffer.get() & 0xFF;
				--length;
				length -= paddingLength;
				state = State.DATA;
				loop = length == 0;
				if (length < 0)
					return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_data_frame_padding");
				break;
			}
			case DATA: {
				int size = Math.min(buffer.remaining(), length);
				int position = buffer.position();
				int limit = buffer.limit();
				buffer.limit(position + size);
				ByteBuffer slice = buffer.slice();
				buffer.limit(limit);
				buffer.position(position + size);

				length -= size;
				if (length == 0) {
					state = State.PADDING;
					loop = paddingLength == 0;
					// Padding bytes include the bytes that define the
					// padding length plus the actual padding bytes.
					onData(slice, false, padding + paddingLength);
				} else {
					// We got partial data, simulate a smaller frame, and stay
					// in DATA state.
					// No padding for these synthetic frames (even if we have
					// read
					// the padding length already), it will be accounted at the
					// end.
					onData(slice, true, 0);
				}
				break;
			}
			case PADDING: {
				int size = Math.min(buffer.remaining(), paddingLength);
				buffer.position(buffer.position() + size);
				paddingLength -= size;
				if (paddingLength == 0) {
					reset();
					return true;
				}
				break;
			}
			default: {
				throw new IllegalStateException();
			}
			}
		}
		return false;
	}

	private void onData(ByteBuffer buffer, boolean fragment, int padding) {
		DataFrame frame = new DataFrame(getStreamId(), buffer, !fragment && isEndStream(), padding);
		notifyData(frame);
	}

	private enum State {
		PREPARE, PADDING_LENGTH, DATA, PADDING
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy