SPDY: remove SPDY/2 support
This commit is contained in:
parent
49587791b7
commit
db59b38db3
@ -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
|
||||
@ -186,23 +186,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
|
||||
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) {
|
||||
@ -427,20 +414,13 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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);
|
||||
|
||||
@ -461,12 +441,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
|
||||
private SpdyHeadersFrame readHeaderBlockFrame(ByteBuf 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;
|
||||
}
|
||||
|
||||
@ -474,18 +452,9 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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);
|
||||
@ -494,8 +463,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
return spdySynStreamFrame;
|
||||
|
||||
case SPDY_SYN_REPLY_FRAME:
|
||||
minLength = version < 3 ? 8 : 4;
|
||||
if (buffer.readableBytes() < minLength) {
|
||||
if (buffer.readableBytes() < 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -503,18 +471,6 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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);
|
||||
|
||||
@ -525,27 +481,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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);
|
||||
|
||||
@ -562,10 +501,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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;
|
||||
@ -577,14 +516,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
||||
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;
|
||||
|
@ -92,12 +92,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
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;
|
||||
out.ensureWritable(SPDY_HEADER_SIZE + length);
|
||||
out.writeShort(version | 0x8000);
|
||||
out.writeShort(SPDY_SYN_STREAM_FRAME);
|
||||
@ -105,19 +100,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
out.writeMedium(length);
|
||||
out.writeInt(spdySynStreamFrame.getStreamId());
|
||||
out.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;
|
||||
}
|
||||
out.writeShort((priority & 0xFF) << 14);
|
||||
} else {
|
||||
out.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
|
||||
}
|
||||
if (version < 3 && data.readableBytes() == 0) {
|
||||
out.writeShort(0);
|
||||
}
|
||||
out.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
|
||||
out.writeBytes(data, data.readerIndex(), headerBlockLength);
|
||||
} finally {
|
||||
data.release();
|
||||
@ -130,25 +113,13 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
try {
|
||||
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;
|
||||
out.ensureWritable(SPDY_HEADER_SIZE + length);
|
||||
out.writeShort(version | 0x8000);
|
||||
out.writeShort(SPDY_SYN_REPLY_FRAME);
|
||||
out.writeByte(flags);
|
||||
out.writeMedium(length);
|
||||
out.writeInt(spdySynReplyFrame.getStreamId());
|
||||
if (version < 3) {
|
||||
if (headerBlockLength == 0) {
|
||||
out.writeInt(0);
|
||||
} else {
|
||||
out.writeShort(0);
|
||||
}
|
||||
}
|
||||
out.writeBytes(data, data.readerIndex(), headerBlockLength);
|
||||
} finally {
|
||||
data.release();
|
||||
@ -178,8 +149,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
out.writeByte(flags);
|
||||
out.writeMedium(length);
|
||||
out.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;
|
||||
@ -187,18 +157,8 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
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
|
||||
out.writeByte(id & 0xFF);
|
||||
out.writeByte(id >> 8 & 0xFF);
|
||||
out.writeByte(id >> 16 & 0xFF);
|
||||
out.writeByte(ID_flags);
|
||||
} else {
|
||||
out.writeByte(ID_flags);
|
||||
out.writeMedium(id);
|
||||
}
|
||||
out.writeByte(ID_flags);
|
||||
out.writeMedium(id);
|
||||
out.writeInt(spdySettingsFrame.getValue(id));
|
||||
}
|
||||
|
||||
@ -214,15 +174,12 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
} else if (msg instanceof SpdyGoAwayFrame) {
|
||||
|
||||
SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
|
||||
int length = version < 3 ? 4 : 8;
|
||||
out.ensureWritable(SPDY_HEADER_SIZE + length);
|
||||
out.ensureWritable(SPDY_HEADER_SIZE + 8);
|
||||
out.writeShort(version | 0x8000);
|
||||
out.writeShort(SPDY_GOAWAY_FRAME);
|
||||
out.writeInt(length);
|
||||
out.writeInt(8);
|
||||
out.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
|
||||
if (version >= 3) {
|
||||
out.writeInt(spdyGoAwayFrame.getStatus().getCode());
|
||||
}
|
||||
out.writeInt(spdyGoAwayFrame.getStatus().getCode());
|
||||
|
||||
} else if (msg instanceof SpdyHeadersFrame) {
|
||||
|
||||
@ -231,21 +188,13 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
|
||||
try {
|
||||
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;
|
||||
out.ensureWritable(SPDY_HEADER_SIZE + length);
|
||||
out.writeShort(version | 0x8000);
|
||||
out.writeShort(SPDY_HEADERS_FRAME);
|
||||
out.writeByte(flags);
|
||||
out.writeMedium(length);
|
||||
out.writeInt(spdyHeadersFrame.getStreamId());
|
||||
if (version < 3 && headerBlockLength != 0) {
|
||||
out.writeShort(0);
|
||||
}
|
||||
out.writeBytes(data, data.readerIndex(), headerBlockLength);
|
||||
} finally {
|
||||
data.release();
|
||||
|
@ -39,7 +39,6 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
|
||||
// TODO: Replace with generic NPN handler
|
||||
|
||||
public enum SelectedProtocol {
|
||||
SPDY_2,
|
||||
SPDY_3,
|
||||
SPDY_3_1,
|
||||
HTTP_1_1,
|
||||
@ -84,9 +83,6 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
|
||||
case UNKNOWN:
|
||||
// Not done with choosing the protocol, so just return here for now,
|
||||
return false;
|
||||
case SPDY_2:
|
||||
addSpdyHandlers(ctx, SpdyVersion.SPDY_2);
|
||||
break;
|
||||
case SPDY_3:
|
||||
addSpdyHandlers(ctx, SpdyVersion.SPDY_3);
|
||||
break;
|
||||
@ -137,8 +133,8 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
|
||||
|
||||
/**
|
||||
* Create the {@link ChannelInboundHandler} that is responsible for handling the http responses
|
||||
* when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_2} or
|
||||
* {@link SelectedProtocol#SPDY_3}.
|
||||
* when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_3} or
|
||||
* {@link SelectedProtocol#SPDY_3_1}.
|
||||
*
|
||||
* By default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
|
||||
* sub-classes may override this to change the behaviour.
|
||||
|
@ -61,7 +61,6 @@ public class SpdySessionHandler
|
||||
private ChannelFutureListener closeSessionFutureListener;
|
||||
|
||||
private final boolean server;
|
||||
private final boolean flowControl;
|
||||
private final boolean sessionFlowControl;
|
||||
|
||||
/**
|
||||
@ -78,7 +77,6 @@ public class SpdySessionHandler
|
||||
throw new NullPointerException("version");
|
||||
}
|
||||
this.server = server;
|
||||
flowControl = version.useFlowControl();
|
||||
sessionFlowControl = version.useSessionFlowControl();
|
||||
}
|
||||
|
||||
@ -165,40 +163,38 @@ public class SpdySessionHandler
|
||||
* 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.content().readableBytes();
|
||||
int newWindowSize = spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
|
||||
// Update receive window size
|
||||
int deltaWindowSize = -1 * spdyDataFrame.content().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)) {
|
||||
spdyDataFrame.release();
|
||||
issueStreamError(ctx, 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)) {
|
||||
spdyDataFrame.release();
|
||||
issueStreamError(ctx, 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.content().readableBytes() > initialReceiveWindowSize) {
|
||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId,
|
||||
spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain());
|
||||
ctx.writeAndFlush(partialDataFrame);
|
||||
}
|
||||
// Window size became negative due to sender writing frame before receiving SETTINGS
|
||||
// Send data frames upstream in initialReceiveWindowSize chunks
|
||||
if (newWindowSize < 0) {
|
||||
while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) {
|
||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId,
|
||||
spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain());
|
||||
ctx.writeAndFlush(partialDataFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
ctx.writeAndFlush(spdyWindowUpdateFrame);
|
||||
}
|
||||
// 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);
|
||||
ctx.writeAndFlush(spdyWindowUpdateFrame);
|
||||
}
|
||||
|
||||
// Close the remote side of the stream if this is the last frame
|
||||
@ -313,12 +309,10 @@ public class SpdySessionHandler
|
||||
}
|
||||
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) {
|
||||
@ -382,28 +376,26 @@ public class SpdySessionHandler
|
||||
* 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, SpdySessionStatus.PROTOCOL_ERROR);
|
||||
} else {
|
||||
issueStreamError(ctx, 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, SpdySessionStatus.PROTOCOL_ERROR);
|
||||
} else {
|
||||
issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
updateSendWindowSize(ctx, streamId, deltaWindowSize);
|
||||
}
|
||||
|
||||
ctx.fireChannelRead(msg);
|
||||
@ -475,65 +467,63 @@ public class SpdySessionHandler
|
||||
* sender must pause transmitting data frames.
|
||||
*/
|
||||
|
||||
if (flowControl) {
|
||||
synchronized (flowControlLock) {
|
||||
int dataLength = spdyDataFrame.content().readableBytes();
|
||||
int sendWindowSize = spdySession.getSendWindowSize(streamId);
|
||||
synchronized (flowControlLock) {
|
||||
int dataLength = spdyDataFrame.content().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, new SpdySession.PendingWrite(spdyDataFrame, promise));
|
||||
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, new SpdySession.PendingWrite(spdyDataFrame, promise));
|
||||
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,
|
||||
spdyDataFrame.content().readSlice(sendWindowSize).retain());
|
||||
|
||||
// Create a partial data frame whose length is the current window size
|
||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId,
|
||||
spdyDataFrame.content().readSlice(sendWindowSize).retain());
|
||||
// Enqueue the remaining data (will be the first frame queued)
|
||||
spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));
|
||||
|
||||
// Enqueue the remaining data (will be the first frame queued)
|
||||
spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));
|
||||
|
||||
// The transfer window size is pre-decremented when sending a data frame downstream.
|
||||
// Close the session on write failures that leave the transfer window in a corrupt state.
|
||||
final ChannelHandlerContext context = ctx;
|
||||
ctx.write(partialDataFrame).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
|
||||
}
|
||||
// The transfer window size is pre-decremented when sending a data frame downstream.
|
||||
// Close the session on write failures that leave the transfer window in a corrupt state.
|
||||
final ChannelHandlerContext context = ctx;
|
||||
ctx.write(partialDataFrame).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
|
||||
}
|
||||
});
|
||||
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 leave the transfer window in a corrupt state.
|
||||
final ChannelHandlerContext context = ctx;
|
||||
promise.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
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 leave the transfer window in a corrupt state.
|
||||
final ChannelHandlerContext context = ctx;
|
||||
promise.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,12 +589,10 @@ public class SpdySessionHandler
|
||||
}
|
||||
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) {
|
||||
|
@ -16,28 +16,14 @@
|
||||
package io.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;
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ public class SpdyFrameDecoderTest {
|
||||
|
||||
@Test
|
||||
public void testTooLargeHeaderNameOnSynStreamRequest() throws Exception {
|
||||
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_2);
|
||||
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_3);
|
||||
testTooLargeHeaderNameOnSynStreamRequest(SpdyVersion.SPDY_3_1);
|
||||
}
|
||||
|
@ -282,8 +282,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyClientSessionHandler() {
|
||||
logger.info("Running: testSpdyClientSessionHandler v2");
|
||||
testSpdySessionHandler(SpdyVersion.SPDY_2, false);
|
||||
logger.info("Running: testSpdyClientSessionHandler v3");
|
||||
testSpdySessionHandler(SpdyVersion.SPDY_3, false);
|
||||
logger.info("Running: testSpdyClientSessionHandler v3.1");
|
||||
@ -292,8 +290,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyClientSessionHandlerPing() {
|
||||
logger.info("Running: testSpdyClientSessionHandlerPing v2");
|
||||
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, false);
|
||||
logger.info("Running: testSpdyClientSessionHandlerPing v3");
|
||||
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, false);
|
||||
logger.info("Running: testSpdyClientSessionHandlerPing v3.1");
|
||||
@ -302,8 +298,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyClientSessionHandlerGoAway() {
|
||||
logger.info("Running: testSpdyClientSessionHandlerGoAway v2");
|
||||
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, false);
|
||||
logger.info("Running: testSpdyClientSessionHandlerGoAway v3");
|
||||
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, false);
|
||||
logger.info("Running: testSpdyClientSessionHandlerGoAway v3.1");
|
||||
@ -312,8 +306,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyServerSessionHandler() {
|
||||
logger.info("Running: testSpdyServerSessionHandler v2");
|
||||
testSpdySessionHandler(SpdyVersion.SPDY_2, true);
|
||||
logger.info("Running: testSpdyServerSessionHandler v3");
|
||||
testSpdySessionHandler(SpdyVersion.SPDY_3, true);
|
||||
logger.info("Running: testSpdyServerSessionHandler v3.1");
|
||||
@ -322,8 +314,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyServerSessionHandlerPing() {
|
||||
logger.info("Running: testSpdyServerSessionHandlerPing v2");
|
||||
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, true);
|
||||
logger.info("Running: testSpdyServerSessionHandlerPing v3");
|
||||
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, true);
|
||||
logger.info("Running: testSpdyServerSessionHandlerPing v3.1");
|
||||
@ -332,8 +322,6 @@ public class SpdySessionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testSpdyServerSessionHandlerGoAway() {
|
||||
logger.info("Running: testSpdyServerSessionHandlerGoAway v2");
|
||||
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, true);
|
||||
logger.info("Running: testSpdyServerSessionHandlerGoAway v3");
|
||||
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, true);
|
||||
logger.info("Running: testSpdyServerSessionHandlerGoAway v3.1");
|
||||
|
@ -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
|
||||
@ -44,8 +44,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
static final int ignoredBytes = 20;
|
||||
|
||||
private static ByteBuf createFrames(int version) {
|
||||
int length = version < 3 ? 1176 : 1174;
|
||||
ByteBuf frames = Unpooled.buffer(length);
|
||||
ByteBuf frames = Unpooled.buffer(1174);
|
||||
|
||||
// SPDY UNKNOWN Frame
|
||||
frames.writeByte(0x80);
|
||||
@ -74,11 +73,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
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);
|
||||
@ -91,11 +86,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
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);
|
||||
@ -116,13 +107,8 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
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
|
||||
@ -136,15 +122,9 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
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);
|
||||
@ -169,10 +149,6 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
|
||||
@Test(timeout = 15000)
|
||||
public void testSpdyEcho() throws Throwable {
|
||||
version = SpdyVersion.SPDY_2;
|
||||
logger.info("Testing against SPDY v2");
|
||||
run();
|
||||
|
||||
version = SpdyVersion.SPDY_3;
|
||||
logger.info("Testing against SPDY v3");
|
||||
run();
|
||||
@ -186,9 +162,6 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
|
||||
|
||||
ByteBuf frames;
|
||||
switch (version) {
|
||||
case SPDY_2:
|
||||
frames = createFrames(2);
|
||||
break;
|
||||
case SPDY_3:
|
||||
case SPDY_3_1:
|
||||
frames = createFrames(3);
|
||||
|
Loading…
Reference in New Issue
Block a user