com.twitter.http2.HttpFrameEncoder Maven / Gradle / Ivy
/*
* Copyright 2015 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twitter.http2;
import java.util.Set;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import static com.twitter.http2.HttpCodecUtil.HTTP_CONTINUATION_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_DATA_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_DEPENDENCY;
import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_ACK;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_HEADERS;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_STREAM;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_PRIORITY;
import static com.twitter.http2.HttpCodecUtil.HTTP_FRAME_HEADER_SIZE;
import static com.twitter.http2.HttpCodecUtil.HTTP_GOAWAY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_HEADERS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_MAX_LENGTH;
import static com.twitter.http2.HttpCodecUtil.HTTP_PING_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PRIORITY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PUSH_PROMISE_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_RST_STREAM_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_SETTINGS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_WINDOW_UPDATE_FRAME;
/**
* Encodes an HTTP/2 Frame into a {@link ByteBuf}.
*/
public class HttpFrameEncoder {
/**
* Encode an HTTP/2 DATA Frame
*/
public ByteBuf encodeDataFrame(int streamId, boolean endStream, ByteBuf data) {
byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
writeFrameHeader(header, data.readableBytes(), HTTP_DATA_FRAME, flags, streamId);
return Unpooled.wrappedBuffer(header, data);
}
/**
* Encode an HTTP/2 HEADERS Frame
*/
public ByteBuf encodeHeadersFrame(
int streamId,
boolean endStream,
boolean exclusive,
int dependency,
int weight,
ByteBuf headerBlock
) {
byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
boolean hasPriority = exclusive
|| dependency != HTTP_DEFAULT_DEPENDENCY || weight != HTTP_DEFAULT_WEIGHT;
if (hasPriority) {
flags |= HTTP_FLAG_PRIORITY;
}
int maxLength = hasPriority ? HTTP_MAX_LENGTH - 5 : HTTP_MAX_LENGTH;
boolean needsContinuations = headerBlock.readableBytes() > maxLength;
if (!needsContinuations) {
flags |= HTTP_FLAG_END_HEADERS;
}
int length = needsContinuations ? maxLength : headerBlock.readableBytes();
if (hasPriority) {
length += 5;
}
int frameLength = hasPriority ? HTTP_FRAME_HEADER_SIZE + 5 : HTTP_FRAME_HEADER_SIZE;
ByteBuf header = Unpooled.buffer(frameLength);
writeFrameHeader(header, length, HTTP_HEADERS_FRAME, flags, streamId);
if (hasPriority) {
if (exclusive) {
header.writeInt(dependency | 0x80000000);
} else {
header.writeInt(dependency);
}
header.writeByte(weight - 1);
length -= 5;
}
ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
if (needsContinuations) {
while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
}
header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
writeFrameHeader(
header,
headerBlock.readableBytes(),
HTTP_CONTINUATION_FRAME,
HTTP_FLAG_END_HEADERS,
streamId
);
frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
}
return frame;
}
/**
* Encode an HTTP/2 PRIORITY Frame
*/
public ByteBuf encodePriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
int length = 5;
byte flags = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_PRIORITY_FRAME, flags, streamId);
if (exclusive) {
frame.writeInt(dependency | 0x80000000);
} else {
frame.writeInt(dependency);
}
frame.writeByte(weight - 1);
return frame;
}
/**
* Encode an HTTP/2 RST_STREAM Frame
*/
public ByteBuf encodeRstStreamFrame(int streamId, int errorCode) {
int length = 4;
byte flags = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_RST_STREAM_FRAME, flags, streamId);
frame.writeInt(errorCode);
return frame;
}
/**
* Encode an HTTP/2 SETTINGS Frame
*/
public ByteBuf encodeSettingsFrame(HttpSettingsFrame httpSettingsFrame) {
Set ids = httpSettingsFrame.getIds();
int length = ids.size() * 6;
byte flags = httpSettingsFrame.isAck() ? HTTP_FLAG_ACK : 0;
int streamId = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_SETTINGS_FRAME, flags, streamId);
for (int id : ids) {
frame.writeShort(id);
frame.writeInt(httpSettingsFrame.getValue(id));
}
return frame;
}
/**
* Encode an HTTP/2 PUSH_PROMISE Frame
*/
public ByteBuf encodePushPromiseFrame(int streamId, int promisedStreamId, ByteBuf headerBlock) {
boolean needsContinuations = headerBlock.readableBytes() > HTTP_MAX_LENGTH - 4;
int length = needsContinuations ? HTTP_MAX_LENGTH - 4 : headerBlock.readableBytes();
byte flags = needsContinuations ? 0 : HTTP_FLAG_END_HEADERS;
ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + 4);
writeFrameHeader(header, length + 4, HTTP_PUSH_PROMISE_FRAME, flags, streamId);
header.writeInt(promisedStreamId);
ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
if (needsContinuations) {
while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
}
header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
writeFrameHeader(
header,
headerBlock.readableBytes(),
HTTP_CONTINUATION_FRAME,
HTTP_FLAG_END_HEADERS,
streamId
);
frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
}
return frame;
}
/**
* Encode an HTTP/2 PING Frame
*/
public ByteBuf encodePingFrame(long data, boolean ack) {
int length = 8;
byte flags = ack ? HTTP_FLAG_ACK : 0;
int streamId = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_PING_FRAME, flags, streamId);
frame.writeLong(data);
return frame;
}
/**
* Encode an HTTP/2 GOAWAY Frame
*/
public ByteBuf encodeGoAwayFrame(int lastStreamId, int errorCode) {
int length = 8;
byte flags = 0;
int streamId = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_GOAWAY_FRAME, flags, streamId);
frame.writeInt(lastStreamId);
frame.writeInt(errorCode);
return frame;
}
/**
* Encode an HTTP/2 WINDOW_UPDATE Frame
*/
public ByteBuf encodeWindowUpdateFrame(int streamId, int windowSizeIncrement) {
int length = 4;
byte flags = 0;
ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
writeFrameHeader(frame, length, HTTP_WINDOW_UPDATE_FRAME, flags, streamId);
frame.writeInt(windowSizeIncrement);
return frame;
}
private void writeFrameHeader(ByteBuf buffer, int length, int type, byte flags, int streamId) {
buffer.writeMedium(length);
buffer.writeByte(type);
buffer.writeByte(flags);
buffer.writeInt(streamId);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy