SPDY: remove SPDY/2 support

This commit is contained in:
Jeff Pinner 2013-10-11 21:18:21 -07:00 committed by Trustin Lee
parent 525dad5d01
commit 75b52ac334
12 changed files with 158 additions and 411 deletions

View File

@ -38,7 +38,7 @@ public class SpdyFrameCodec implements ChannelUpstreamHandler,
* {@code compressionLevel (6)}, {@code windowBits (15)},
* and {@code memLevel (8)}).
*/
public SpdyFrameCodec(int version) {
public SpdyFrameCodec(SpdyVersion version) {
this(version, 8192, 16384, 6, 15, 8);
}
@ -46,7 +46,7 @@ public class SpdyFrameCodec implements ChannelUpstreamHandler,
* Creates a new instance with the specified decoder and encoder options.
*/
public SpdyFrameCodec(
int version, int maxChunkSize, int maxHeaderSize,
SpdyVersion version, int maxChunkSize, int maxHeaderSize,
int compressionLevel, int windowBits, int memLevel) {
decoder = new SpdyFrameDecoder(version, maxChunkSize, maxHeaderSize);
encoder = new SpdyFrameEncoder(version, compressionLevel, windowBits, memLevel);

View File

@ -58,15 +58,6 @@ public class SpdyFrameDecoder extends FrameDecoder {
FRAME_ERROR
}
/**
* Creates a new instance with the specified {@code version} and the default
* {@code maxChunkSize (8192)} and {@code maxHeaderSize (16384)}.
*/
@Deprecated
public SpdyFrameDecoder(int version) {
this(SpdyVersion.valueOf(version), 8192, 16384);
}
/**
* Creates a new instance with the specified {@code version} and the default
* {@code maxChunkSize (8192)} and {@code maxHeaderSize (16384)}.
@ -75,14 +66,6 @@ public class SpdyFrameDecoder extends FrameDecoder {
this(spdyVersion, 8192, 16384);
}
/**
* Creates a new instance with the specified parameters.
*/
@Deprecated
public SpdyFrameDecoder(int version, int maxChunkSize, int maxHeaderSize) {
this(SpdyVersion.valueOf(version), maxChunkSize, maxHeaderSize);
}
/**
* Creates a new instance with the specified parameters.
*/
@ -185,23 +168,10 @@ public class SpdyFrameDecoder extends FrameDecoder {
int readableEntries = Math.min(buffer.readableBytes() >> 3, length >> 3);
for (int i = 0; i < readableEntries; i ++) {
int ID;
byte ID_flags;
if (version < 3) {
// Chromium Issue 79156
// SPDY setting ids are not written in network byte order
// Read id assuming the architecture is little endian
ID = buffer.readByte() & 0xFF |
(buffer.readByte() & 0xFF) << 8 |
(buffer.readByte() & 0xFF) << 16;
ID_flags = buffer.readByte();
} else {
ID_flags = buffer.readByte();
ID = getUnsignedMedium(buffer, buffer.readerIndex());
buffer.skipBytes(3);
}
int value = getSignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
byte ID_flags = buffer.getByte(buffer.readerIndex());
int ID = getUnsignedMedium(buffer, buffer.readerIndex() + 1);
int value = getSignedInt(buffer, buffer.readerIndex() + 4);
buffer.skipBytes(8);
// Check for invalid ID -- avoid IllegalArgumentException in setValue
if (ID == 0) {
@ -418,20 +388,13 @@ public class SpdyFrameDecoder extends FrameDecoder {
return new DefaultSpdyPingFrame(ID);
case SPDY_GOAWAY_FRAME:
int minLength = version < 3 ? 4 : 8;
if (buffer.readableBytes() < minLength) {
if (buffer.readableBytes() < 8) {
return null;
}
int lastGoodStreamID = getUnsignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
if (version < 3) {
return new DefaultSpdyGoAwayFrame(lastGoodStreamID);
}
statusCode = getSignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
buffer.skipBytes(8);
return new DefaultSpdyGoAwayFrame(lastGoodStreamID, statusCode);
@ -452,12 +415,10 @@ public class SpdyFrameDecoder extends FrameDecoder {
}
private SpdyHeadersFrame readHeaderBlockFrame(ChannelBuffer buffer) {
int minLength;
int streamId;
switch (type) {
case SPDY_SYN_STREAM_FRAME:
minLength = version < 3 ? 12 : 10;
if (buffer.readableBytes() < minLength) {
if (buffer.readableBytes() < 10) {
return null;
}
@ -465,18 +426,9 @@ public class SpdyFrameDecoder extends FrameDecoder {
streamId = getUnsignedInt(buffer, offset);
int associatedToStreamID = getUnsignedInt(buffer, offset + 4);
byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
if (version < 3) {
priority >>= 1;
}
buffer.skipBytes(10);
length -= 10;
// SPDY/2 requires 16-bits of padding for empty header blocks
if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
buffer.skipBytes(2);
length = 0;
}
SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamId, associatedToStreamID, priority);
spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -485,8 +437,7 @@ public class SpdyFrameDecoder extends FrameDecoder {
return spdySynStreamFrame;
case SPDY_SYN_REPLY_FRAME:
minLength = version < 3 ? 8 : 4;
if (buffer.readableBytes() < minLength) {
if (buffer.readableBytes() < 4) {
return null;
}
@ -494,18 +445,6 @@ public class SpdyFrameDecoder extends FrameDecoder {
buffer.skipBytes(4);
length -= 4;
// SPDY/2 has 16-bits of unused space
if (version < 3) {
buffer.skipBytes(2);
length -= 2;
}
// SPDY/2 requires 16-bits of padding for empty header blocks
if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
buffer.skipBytes(2);
length = 0;
}
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -516,27 +455,10 @@ public class SpdyFrameDecoder extends FrameDecoder {
return null;
}
// SPDY/2 allows length 4 frame when there are no name/value pairs
if (version < 3 && length > 4 && buffer.readableBytes() < 8) {
return null;
}
streamId = getUnsignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
length -= 4;
// SPDY/2 has 16-bits of unused space
if (version < 3 && length != 0) {
buffer.skipBytes(2);
length -= 2;
}
// SPDY/2 requires 16-bits of padding for empty header blocks
if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
buffer.skipBytes(2);
length = 0;
}
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId);
spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -553,10 +475,10 @@ public class SpdyFrameDecoder extends FrameDecoder {
return streamId != 0;
case SPDY_SYN_STREAM_FRAME:
return version < 3 ? length >= 12 : length >= 10;
return length >= 10;
case SPDY_SYN_REPLY_FRAME:
return version < 3 ? length >= 8 : length >= 4;
return length >= 4;
case SPDY_RST_STREAM_FRAME:
return flags == 0 && length == 8;
@ -568,14 +490,10 @@ public class SpdyFrameDecoder extends FrameDecoder {
return length == 4;
case SPDY_GOAWAY_FRAME:
return version < 3 ? length == 4 : length == 8;
return length == 8;
case SPDY_HEADERS_FRAME:
if (version < 3) {
return length == 4 || length >= 8;
} else {
return length >= 4;
}
return length >= 4;
case SPDY_WINDOW_UPDATE_FRAME:
return length == 8;

View File

@ -37,16 +37,6 @@ 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)}.
*/
@Deprecated
public SpdyFrameEncoder(int version) {
this(SpdyVersion.valueOf(version), 6, 15, 8);
}
/**
* Creates a new instance with the specified {@code version} and the
* default {@code compressionLevel (6)}, {@code windowBits (15)},
@ -56,14 +46,6 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
this(spdyVersion, 6, 15, 8);
}
/**
* Creates a new instance with the specified parameters.
*/
@Deprecated
public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
this(SpdyVersion.valueOf(version), compressionLevel, windowBits, memLevel);
}
/**
* Creates a new instance with the specified parameters.
*/
@ -129,33 +111,16 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
flags |= SPDY_FLAG_UNIDIRECTIONAL;
}
int headerBlockLength = data.readableBytes();
int length;
if (version < 3) {
length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
} else {
length = 10 + headerBlockLength;
}
int length = 10 + headerBlockLength;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 10);
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);
}
frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
// Writes of compressed data must occur in order
final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
@ -170,26 +135,14 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
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;
}
int length = 4 + headerBlockLength;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
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());
@ -226,8 +179,7 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
frame.writeByte(flags);
frame.writeMedium(length);
frame.writeInt(numEntries);
for (Integer ID: IDs) {
int id = ID.intValue();
for (Integer id: IDs) {
byte ID_flags = 0;
if (spdySettingsFrame.isPersistValue(id)) {
ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
@ -235,18 +187,8 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
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.writeByte(ID_flags);
frame.writeMedium(id);
frame.writeInt(spdySettingsFrame.getValue(id));
}
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
@ -269,16 +211,13 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
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);
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
frame.writeShort(version | 0x8000);
frame.writeShort(SPDY_GOAWAY_FRAME);
frame.writeInt(length);
frame.writeInt(8);
frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
if (version >= 3) {
frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
}
frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
return;
}
@ -290,22 +229,14 @@ public class SpdyFrameEncoder implements ChannelDownstreamHandler {
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;
}
int length = 4 + headerBlockLength;
ChannelBuffer frame = ChannelBuffers.buffer(
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
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());

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012 The Netty Project
* 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
@ -33,7 +33,7 @@ public class SpdyHttpCodec implements ChannelUpstreamHandler, ChannelDownstreamH
/**
* Creates a new instance with the specified decoder options.
*/
public SpdyHttpCodec(int version, int maxContentLength) {
public SpdyHttpCodec(SpdyVersion version, int maxContentLength) {
decoder = new SpdyHttpDecoder(version, maxContentLength);
encoder = new SpdyHttpEncoder(version);
}

View File

@ -47,19 +47,6 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
private final int maxContentLength;
private final Map<Integer, HttpMessage> messageMap;
/**
* Creates a new instance.
*
* @param version the protocol version
* @param maxContentLength the maximum length of the message content.
* If the length of the message content exceeds this value,
* a {@link TooLongFrameException} will be raised.
*/
@Deprecated
public SpdyHttpDecoder(int version, int maxContentLength) {
this(SpdyVersion.valueOf(version), maxContentLength, new HashMap<Integer, HttpMessage>());
}
/**
* Creates a new instance.
*
@ -68,7 +55,6 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
* If the length of the message content exceeds this value,
* a {@link TooLongFrameException} will be raised.
*/
@Deprecated
public SpdyHttpDecoder(SpdyVersion spdyVersion, int maxContentLength) {
this(spdyVersion, maxContentLength, new HashMap<Integer, HttpMessage>());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012 The Netty Project
* 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
@ -131,16 +131,6 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler {
private final int spdyVersion;
private volatile int currentStreamId;
/**
* Creates a new instance.
*
* @param version the protocol version
*/
@Deprecated
public SpdyHttpEncoder(int version) {
this(SpdyVersion.valueOf(version));
}
/**
* Creates a new instance.
*

View File

@ -37,7 +37,7 @@ import org.jboss.netty.handler.ssl.SslHandler;
public abstract class SpdyOrHttpChooser implements ChannelUpstreamHandler {
public enum SelectedProtocol {
SpdyVersion2,
SpdyVersion3_1,
SpdyVersion3,
HttpVersion1_1,
HttpVersion1_0,
@ -73,11 +73,11 @@ public abstract class SpdyOrHttpChooser implements ChannelUpstreamHandler {
case None:
// Not done with choosing the protocol, so just return here for now,
return;
case SpdyVersion2:
addSpdyHandlers(ctx, 2);
break;
case SpdyVersion3:
addSpdyHandlers(ctx, 3);
addSpdyHandlers(ctx, SpdyVersion.SPDY_3);
break;
case SpdyVersion3_1:
addSpdyHandlers(ctx, SpdyVersion.SPDY_3_1);
break;
case HttpVersion1_0:
case HttpVersion1_1:
@ -95,7 +95,7 @@ public abstract class SpdyOrHttpChooser implements ChannelUpstreamHandler {
/**
* Add all {@link ChannelHandler}'s that are needed for SPDY with the given version.
*/
protected void addSpdyHandlers(ChannelHandlerContext ctx, int version) {
protected void addSpdyHandlers(ChannelHandlerContext ctx, SpdyVersion version) {
ChannelPipeline pipeline = ctx.getPipeline();
pipeline.addLast("spdyDecoder", new SpdyFrameDecoder(version));
pipeline.addLast("spdyEncoder", new SpdyFrameEncoder(version));
@ -126,8 +126,8 @@ public abstract class SpdyOrHttpChooser implements ChannelUpstreamHandler {
/**
* Create the {@link ChannelUpstreamHandler} that is responsible for handling the {@link HttpRequest}'s
* when the {@link SelectedProtocol} was {@link SelectedProtocol#SpdyVersion2} or
* {@link SelectedProtocol#SpdyVersion3}.
* when the {@link SelectedProtocol} was {@link SelectedProtocol#SpdyVersion3} or
* {@link SelectedProtocol#SpdyVersion3_1}.
*
* Bye default this method will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
* sub-classes may override this to change the behaviour.

View File

@ -62,23 +62,8 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
private volatile ChannelFutureListener closeSessionFutureListener;
private final boolean server;
private final boolean flowControl;
private final boolean sessionFlowControl;
/**
* Creates a new session handler.
*
* @param version the protocol version
* @param server {@code true} if and only if this session handler should
* handle the server endpoint of the connection.
* {@code false} if and only if this session handler should
* handle the client endpoint of the connection.
*/
@Deprecated
public SpdySessionHandler(int version, boolean server) {
this(SpdyVersion.valueOf(version), server);
}
/**
* Creates a new session handler.
*
@ -93,7 +78,6 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
throw new NullPointerException("spdyVersion");
}
this.server = server;
flowControl = spdyVersion.useFlowControl();
sessionFlowControl = spdyVersion.useSessionFlowControl();
}
@ -180,40 +164,38 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
* Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
*/
if (flowControl) {
// Update receive window size
int deltaWindowSize = -1 * spdyDataFrame.getData().readableBytes();
int newWindowSize = spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
// Update receive window size
int deltaWindowSize = -1 * spdyDataFrame.getData().readableBytes();
int newWindowSize = spdySession.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 session when writing the SETTINGS frame
// and is cleared once we send a WINDOW_UPDATE frame.
if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) {
issueStreamError(ctx, e.getRemoteAddress(), streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
return;
}
// 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 session when writing the SETTINGS frame
// and is cleared once we send a WINDOW_UPDATE frame.
if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) {
issueStreamError(ctx, e.getRemoteAddress(), streamId, SpdyStreamStatus.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 (spdyDataFrame.getData().readableBytes() > initialReceiveWindowSize) {
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId);
partialDataFrame.setData(spdyDataFrame.getData().readSlice(initialReceiveWindowSize));
Channels.fireMessageReceived(ctx, partialDataFrame, e.getRemoteAddress());
}
// Window size became negative due to sender writing frame before receiving SETTINGS
// Send data frames upstream in initialReceiveWindowSize chunks
if (newWindowSize < 0) {
while (spdyDataFrame.getData().readableBytes() > initialReceiveWindowSize) {
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId);
partialDataFrame.setData(spdyDataFrame.getData().readSlice(initialReceiveWindowSize));
Channels.fireMessageReceived(ctx, partialDataFrame, e.getRemoteAddress());
}
}
// Send a WINDOW_UPDATE frame if less than half the stream window size remains
if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) {
deltaWindowSize = initialReceiveWindowSize - newWindowSize;
spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
SpdyWindowUpdateFrame spdyWindowUpdateFrame =
new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
Channels.write(
ctx, Channels.future(e.getChannel()), spdyWindowUpdateFrame, e.getRemoteAddress());
}
// Send a WINDOW_UPDATE frame if less than half the stream window size remains
if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) {
deltaWindowSize = initialReceiveWindowSize - newWindowSize;
spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
SpdyWindowUpdateFrame spdyWindowUpdateFrame =
new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
Channels.write(
ctx, Channels.future(e.getChannel()), spdyWindowUpdateFrame, e.getRemoteAddress());
}
// Close the remote side of the stream if this is the last frame
@ -328,12 +310,10 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
}
spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
if (flowControl) {
int newInitialWindowSize =
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
if (newInitialWindowSize >= 0) {
updateInitialSendWindowSize(newInitialWindowSize);
}
int newInitialWindowSize =
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
if (newInitialWindowSize >= 0) {
updateInitialSendWindowSize(newInitialWindowSize);
}
} else if (msg instanceof SpdyPingFrame) {
@ -397,28 +377,26 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
* after sending the last frame for the stream.
*/
if (flowControl) {
SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
int streamId = spdyWindowUpdateFrame.getStreamId();
int deltaWindowSize = spdyWindowUpdateFrame.getDeltaWindowSize();
SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
int streamId = spdyWindowUpdateFrame.getStreamId();
int deltaWindowSize = spdyWindowUpdateFrame.getDeltaWindowSize();
// Ignore frames for half-closed streams
if (streamId != SPDY_SESSION_STREAM_ID && spdySession.isLocalSideClosed(streamId)) {
return;
}
// Check for numerical overflow
if (spdySession.getSendWindowSize(streamId) > Integer.MAX_VALUE - deltaWindowSize) {
if (streamId == SPDY_SESSION_STREAM_ID) {
issueSessionError(ctx, e.getChannel(), e.getRemoteAddress(), SpdySessionStatus.PROTOCOL_ERROR);
} else {
issueStreamError(ctx, e.getRemoteAddress(), streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
}
return;
}
updateSendWindowSize(ctx, streamId, deltaWindowSize);
// Ignore frames for half-closed streams
if (streamId != SPDY_SESSION_STREAM_ID && spdySession.isLocalSideClosed(streamId)) {
return;
}
// Check for numerical overflow
if (spdySession.getSendWindowSize(streamId) > Integer.MAX_VALUE - deltaWindowSize) {
if (streamId == SPDY_SESSION_STREAM_ID) {
issueSessionError(ctx, e.getChannel(), e.getRemoteAddress(), SpdySessionStatus.PROTOCOL_ERROR);
} else {
issueStreamError(ctx, e.getRemoteAddress(), streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
}
return;
}
updateSendWindowSize(ctx, streamId, deltaWindowSize);
return;
}
@ -490,73 +468,71 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
* sender must pause transmitting data frames.
*/
if (flowControl) {
synchronized (flowControlLock) {
int dataLength = spdyDataFrame.getData().readableBytes();
int sendWindowSize = spdySession.getSendWindowSize(streamId);
synchronized (flowControlLock) {
int dataLength = spdyDataFrame.getData().readableBytes();
int sendWindowSize = spdySession.getSendWindowSize(streamId);
if (sessionFlowControl) {
int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID);
sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);
}
if (sendWindowSize <= 0) {
// Stream is stalled -- enqueue Data frame and return
spdySession.putPendingWrite(streamId, e);
return;
} else if (sendWindowSize < dataLength) {
// Stream is not stalled but we cannot send the entire frame
spdySession.updateSendWindowSize(streamId, -1 * sendWindowSize);
if (sessionFlowControl) {
int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID);
sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);
spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);
}
if (sendWindowSize <= 0) {
// Stream is stalled -- enqueue Data frame and return
spdySession.putPendingWrite(streamId, e);
return;
} else if (sendWindowSize < dataLength) {
// Stream is not stalled but we cannot send the entire frame
spdySession.updateSendWindowSize(streamId, -1 * sendWindowSize);
if (sessionFlowControl) {
spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);
}
// Create a partial data frame whose length is the current window size
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId);
partialDataFrame.setData(spdyDataFrame.getData().readSlice(sendWindowSize));
// Create a partial data frame whose length is the current window size
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId);
partialDataFrame.setData(spdyDataFrame.getData().readSlice(sendWindowSize));
// Enqueue the remaining data (will be the first frame queued)
spdySession.putPendingWrite(streamId, e);
// Enqueue the remaining data (will be the first frame queued)
spdySession.putPendingWrite(streamId, e);
ChannelFuture writeFuture = Channels.future(e.getChannel());
ChannelFuture writeFuture = Channels.future(e.getChannel());
// The transfer window size is pre-decremented when sending a data frame downstream.
// Close the session on write failures that leaves the transfer window in a corrupt state.
final SocketAddress remoteAddress = e.getRemoteAddress();
final ChannelHandlerContext context = ctx;
e.getFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
Channel channel = future.getChannel();
issueSessionError(
context, channel, remoteAddress, SpdySessionStatus.INTERNAL_ERROR);
}
// The transfer window size is pre-decremented when sending a data frame downstream.
// Close the session on write failures that leaves the transfer window in a corrupt state.
final SocketAddress remoteAddress = e.getRemoteAddress();
final ChannelHandlerContext context = ctx;
e.getFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
Channel channel = future.getChannel();
issueSessionError(
context, channel, remoteAddress, SpdySessionStatus.INTERNAL_ERROR);
}
});
Channels.write(ctx, writeFuture, partialDataFrame, remoteAddress);
return;
} else {
// Window size is large enough to send entire data frame
spdySession.updateSendWindowSize(streamId, -1 * dataLength);
if (sessionFlowControl) {
spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);
}
});
// The transfer window size is pre-decremented when sending a data frame downstream.
// Close the session on write failures that leaves the transfer window in a corrupt state.
final SocketAddress remoteAddress = e.getRemoteAddress();
final ChannelHandlerContext context = ctx;
e.getFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
Channel channel = future.getChannel();
issueSessionError(
context, channel, remoteAddress, SpdySessionStatus.INTERNAL_ERROR);
}
}
});
Channels.write(ctx, writeFuture, partialDataFrame, remoteAddress);
return;
} else {
// Window size is large enough to send entire data frame
spdySession.updateSendWindowSize(streamId, -1 * dataLength);
if (sessionFlowControl) {
spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);
}
// The transfer window size is pre-decremented when sending a data frame downstream.
// Close the session on write failures that leaves the transfer window in a corrupt state.
final SocketAddress remoteAddress = e.getRemoteAddress();
final ChannelHandlerContext context = ctx;
e.getFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
Channel channel = future.getChannel();
issueSessionError(
context, channel, remoteAddress, SpdySessionStatus.INTERNAL_ERROR);
}
}
});
}
}
@ -622,12 +598,10 @@ public class SpdySessionHandler extends SimpleChannelUpstreamHandler
}
spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
if (flowControl) {
int newInitialWindowSize =
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
if (newInitialWindowSize >= 0) {
updateInitialReceiveWindowSize(newInitialWindowSize);
}
int newInitialWindowSize =
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
if (newInitialWindowSize >= 0) {
updateInitialReceiveWindowSize(newInitialWindowSize);
}
} else if (msg instanceof SpdyPingFrame) {

View File

@ -16,28 +16,14 @@
package org.jboss.netty.handler.codec.spdy;
public enum SpdyVersion {
SPDY_2 (2, false, false),
SPDY_3 (3, true, false),
SPDY_3_1 (3, true, true);
static SpdyVersion valueOf(int version) {
if (version == 2) {
return SPDY_2;
}
if (version == 3) {
return SPDY_3;
}
throw new IllegalArgumentException(
"unsupported version: " + version);
}
SPDY_3 (3, false),
SPDY_3_1 (3, true);
private final int version;
private final boolean flowControl;
private final boolean sessionFlowControl;
private SpdyVersion(int version, boolean flowControl, boolean sessionFlowControl) {
private SpdyVersion(int version, boolean sessionFlowControl) {
this.version = version;
this.flowControl = flowControl;
this.sessionFlowControl = sessionFlowControl;
}
@ -45,10 +31,6 @@ public enum SpdyVersion {
return version;
}
boolean useFlowControl() {
return flowControl;
}
boolean useSessionFlowControl() {
return sessionFlowControl;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012 The Netty Project
* 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
@ -46,8 +46,7 @@ public abstract class AbstractSocketSpdyEchoTest {
static final int ignoredBytes = 20;
private static ChannelBuffer createFrames(int version) {
int length = version < 3 ? 1176 : 1174;
ChannelBuffer frames = ChannelBuffers.buffer(length);
ChannelBuffer frames = ChannelBuffers.buffer(1174);
// SPDY UNKNOWN Frame
frames.writeByte(0x80);
@ -76,32 +75,18 @@ public abstract class AbstractSocketSpdyEchoTest {
frames.writeByte(version);
frames.writeShort(1);
frames.writeByte(0x03);
if (version < 3) {
frames.writeMedium(12);
} else {
frames.writeMedium(10);
}
frames.writeMedium(10);
frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeShort(0x8000);
if (version < 3) {
frames.writeShort(0);
}
// SPDY SYN_REPLY Frame
frames.writeByte(0x80);
frames.writeByte(version);
frames.writeShort(2);
frames.writeByte(0x01);
if (version < 3) {
frames.writeMedium(8);
} else {
frames.writeMedium(4);
}
frames.writeMedium(4);
frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01);
if (version < 3) {
frames.writeInt(0);
}
// SPDY RST_STREAM Frame
frames.writeByte(0x80);
@ -118,13 +103,8 @@ public abstract class AbstractSocketSpdyEchoTest {
frames.writeByte(0x01);
frames.writeMedium(12);
frames.writeInt(1);
if (version < 3) {
frames.writeMedium(random.nextInt());
frames.writeByte(0x03);
} else {
frames.writeByte(0x03);
frames.writeMedium(random.nextInt());
}
frames.writeByte(0x03);
frames.writeMedium(random.nextInt());
frames.writeInt(random.nextInt());
// SPDY PING Frame
@ -138,15 +118,9 @@ public abstract class AbstractSocketSpdyEchoTest {
frames.writeByte(0x80);
frames.writeByte(version);
frames.writeShort(7);
if (version < 3) {
frames.writeInt(4);
} else {
frames.writeInt(8);
}
frames.writeInt(8);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
if (version >= 3) {
frames.writeInt(random.nextInt() | 0x01);
}
frames.writeInt(random.nextInt() | 0x01);
// SPDY HEADERS Frame
frames.writeByte(0x80);
@ -173,7 +147,6 @@ public abstract class AbstractSocketSpdyEchoTest {
@Test
public void testSpdyEcho() throws Throwable {
testSpdyEcho(SpdyVersion.SPDY_2);
testSpdyEcho(SpdyVersion.SPDY_3);
testSpdyEcho(SpdyVersion.SPDY_3_1);
}

View File

@ -41,7 +41,6 @@ public class SpdyFrameDecoderTest {
@Test
public void testTooLargeHeaderNameOnSynStreamRequest() throws Exception {
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_2);
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_3);
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_3_1);
}

View File

@ -200,42 +200,36 @@ public class SpdySessionHandlerTest {
@Test
public void testSpdyClientSessionHandler() {
testSpdySessionHandler(SpdyVersion.SPDY_2, false);
testSpdySessionHandler(SpdyVersion.SPDY_3, false);
testSpdySessionHandler(SpdyVersion.SPDY_3_1, false);
}
@Test
public void testSpdyClientSessionHandlerPing() {
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, false);
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, false);
testSpdySessionHandlerPing(SpdyVersion.SPDY_3_1, false);
}
@Test
public void testSpdyClientSessionHandlerGoAway() {
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, false);
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, false);
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3_1, false);
}
@Test
public void testSpdyServerSessionHandler() {
testSpdySessionHandler(SpdyVersion.SPDY_2, true);
testSpdySessionHandler(SpdyVersion.SPDY_3, true);
testSpdySessionHandler(SpdyVersion.SPDY_3_1, true);
}
@Test
public void testSpdyServerSessionHandlerPing() {
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, true);
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, true);
testSpdySessionHandlerPing(SpdyVersion.SPDY_3_1, true);
}
@Test
public void testSpdyServerSessionHandlerGoAway() {
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, true);
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, true);
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3_1, true);
}