org.jboss.netty.handler.codec.spdy.SpdyFrameEncoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netty Show documentation
Show all versions of netty Show documentation
The Netty project is an effort to provide an asynchronous event-driven
network application framework and tools for rapid development of
maintainable high performance and high scalability protocol servers and
clients. In other words, Netty is a NIO client server framework which
enables quick and easy development of network applications such as protocol
servers and clients. It greatly simplifies and streamlines network
programming such as TCP and UDP socket server.
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 org.jboss.netty.handler.codec.spdy;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelDownstreamHandler;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import java.nio.ByteOrder;
import java.util.Set;
import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
/**
* Encodes a SPDY Frame into a {@link ChannelBuffer}.
*/
public class SpdyFrameEncoder implements ChannelDownstreamHandler {
private final int version;
private final SpdyHeaderBlockEncoder headerBlockEncoder;
/**
* Creates a new instance with the specified {@code version} and the
* default {@code compressionLevel (6)}, {@code windowBits (15)},
* and {@code memLevel (8)}.
*/
public SpdyFrameEncoder(int version) {
this(version, 6, 15, 8);
}
/**
* Creates a new instance with the specified parameters.
*/
public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
this(version, SpdyHeaderBlockEncoder.newInstance(
version, compressionLevel, windowBits, memLevel));
}
protected SpdyFrameEncoder(int version, SpdyHeaderBlockEncoder headerBlockEncoder) {
if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
throw new IllegalArgumentException(
"unknown version: " + version);
}
this.version = version;
this.headerBlockEncoder = headerBlockEncoder;
}
public void handleDownstream(
final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
if (evt instanceof ChannelStateEvent) {
ChannelStateEvent e = (ChannelStateEvent) evt;
switch (e.getState()) {
case OPEN:
case CONNECTED:
case BOUND:
if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
synchronized (headerBlockEncoder) {
headerBlockEncoder.end();
}
}
}
}
if (!(evt instanceof MessageEvent)) {
ctx.sendDownstream(evt);
return;
}
final MessageEvent e = (MessageEvent) evt;
Object msg = e.getMessage();
if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
ChannelBuffer data = spdyDataFrame.getData();
byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
ChannelBuffer header = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
header.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
header.writeByte(flags);
header.writeMedium(data.readableBytes());
ChannelBuffer frame = ChannelBuffers.wrappedBuffer(header, data);
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
if (msg instanceof SpdySynStreamFrame) {
synchronized (headerBlockEncoder) {
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
ChannelBuffer data = headerBlockEncoder.encode(spdySynStreamFrame);
byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
if (spdySynStreamFrame.isUnidirectional()) {
flags |= SPDY_FLAG_UNIDIRECTIONAL;
}
int headerBlockLength = data.readableBytes();
int length;
if (version < 3) {
length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
} else {
length = 10 + headerBlockLength;
}
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_SYN_STREAM_FRAME);
frame.writeByte(flags);
frame.writeMedium(length);
frame.writeInt(spdySynStreamFrame.getStreamId());
frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
if (version < 3) {
// Restrict priorities for SPDY/2 to between 0 and 3
byte priority = spdySynStreamFrame.getPriority();
if (priority > 3) {
priority = 3;
}
frame.writeShort((priority & 0xFF) << 14);
} else {
frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
}
if (version < 3 && data.readableBytes() == 0) {
frame.writeShort(0);
}
// Writes of compressed data must occur in order
final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
}
return;
}
if (msg instanceof SpdySynReplyFrame) {
synchronized (headerBlockEncoder) {
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
ChannelBuffer data = headerBlockEncoder.encode(spdySynReplyFrame);
byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
int headerBlockLength = data.readableBytes();
int length;
if (version < 3) {
length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
} else {
length = 4 + headerBlockLength;
}
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_SYN_REPLY_FRAME);
frame.writeByte(flags);
frame.writeMedium(length);
frame.writeInt(spdySynReplyFrame.getStreamId());
if (version < 3) {
if (data.readableBytes() == 0) {
frame.writeInt(0);
} else {
frame.writeShort(0);
}
}
// Writes of compressed data must occur in order
final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
}
return;
}
if (msg instanceof SpdyRstStreamFrame) {
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_RST_STREAM_FRAME);
frame.writeInt(8);
frame.writeInt(spdyRstStreamFrame.getStreamId());
frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
if (msg instanceof SpdySettingsFrame) {
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
SPDY_SETTINGS_CLEAR : 0;
Set IDs = spdySettingsFrame.getIds();
int numEntries = IDs.size();
int length = 4 + numEntries * 8;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_SETTINGS_FRAME);
frame.writeByte(flags);
frame.writeMedium(length);
frame.writeInt(numEntries);
for (Integer ID: IDs) {
int id = ID.intValue();
byte ID_flags = 0;
if (spdySettingsFrame.isPersistValue(id)) {
ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
}
if (spdySettingsFrame.isPersisted(id)) {
ID_flags |= SPDY_SETTINGS_PERSISTED;
}
if (version < 3) {
// Chromium Issue 79156
// SPDY setting ids are not written in network byte order
// Write id assuming the architecture is little endian
frame.writeByte(id & 0xFF);
frame.writeByte(id >> 8 & 0xFF);
frame.writeByte(id >> 16 & 0xFF);
frame.writeByte(ID_flags);
} else {
frame.writeByte(ID_flags);
frame.writeMedium(id);
}
frame.writeInt(spdySettingsFrame.getValue(id));
}
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
if (msg instanceof SpdyPingFrame) {
SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_PING_FRAME);
frame.writeInt(4);
frame.writeInt(spdyPingFrame.getId());
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
if (msg instanceof SpdyGoAwayFrame) {
SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
int length = version < 3 ? 4 : 8;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_GOAWAY_FRAME);
frame.writeInt(length);
frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
if (version >= 3) {
frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
}
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
if (msg instanceof SpdyHeadersFrame) {
synchronized (headerBlockEncoder) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
ChannelBuffer data = headerBlockEncoder.encode(spdyHeadersFrame);
byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
int headerBlockLength = data.readableBytes();
int length;
if (version < 3) {
length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
} else {
length = 4 + headerBlockLength;
}
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_HEADERS_FRAME);
frame.writeByte(flags);
frame.writeMedium(length);
frame.writeInt(spdyHeadersFrame.getStreamId());
if (version < 3 && data.readableBytes() != 0) {
frame.writeShort(0);
}
// Writes of compressed data must occur in order
final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
}
return;
}
if (msg instanceof SpdyWindowUpdateFrame) {
SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
frame.writeInt(8);
frame.writeInt(spdyWindowUpdateFrame.getStreamId());
frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
// Unknown message type
ctx.sendDownstream(evt);
}
}