Merge pull request #1921 from jpinner/kill_spdy_2

SPDY: remove SPDY/2 support
This commit is contained in:
Bill Gallagher 2013-10-15 09:30:55 -07:00
commit ef139306af
8 changed files with 139 additions and 329 deletions

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, * 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 * 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); int readableEntries = Math.min(buffer.readableBytes() >> 3, length >> 3);
for (int i = 0; i < readableEntries; i ++) { for (int i = 0; i < readableEntries; i ++) {
int ID; byte ID_flags = buffer.getByte(buffer.readerIndex());
byte ID_flags; int ID = getUnsignedMedium(buffer, buffer.readerIndex() + 1);
if (version < 3) { int value = getSignedInt(buffer, buffer.readerIndex() + 4);
// Chromium Issue 79156 buffer.skipBytes(8);
// 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);
// Check for invalid ID -- avoid IllegalArgumentException in setValue // Check for invalid ID -- avoid IllegalArgumentException in setValue
if (ID == 0) { if (ID == 0) {
@ -427,20 +414,13 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
return new DefaultSpdyPingFrame(ID); return new DefaultSpdyPingFrame(ID);
case SPDY_GOAWAY_FRAME: case SPDY_GOAWAY_FRAME:
int minLength = version < 3 ? 4 : 8; if (buffer.readableBytes() < 8) {
if (buffer.readableBytes() < minLength) {
return null; return null;
} }
int lastGoodStreamId = getUnsignedInt(buffer, buffer.readerIndex()); int lastGoodStreamId = getUnsignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4); statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
buffer.skipBytes(8);
if (version < 3) {
return new DefaultSpdyGoAwayFrame(lastGoodStreamId);
}
statusCode = getSignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
return new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode); return new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
@ -461,12 +441,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
} }
private SpdyHeadersFrame readHeaderBlockFrame(ByteBuf buffer) { private SpdyHeadersFrame readHeaderBlockFrame(ByteBuf buffer) {
int minLength;
int streamId; int streamId;
switch (type) { switch (type) {
case SPDY_SYN_STREAM_FRAME: case SPDY_SYN_STREAM_FRAME:
minLength = version < 3 ? 12 : 10; if (buffer.readableBytes() < 10) {
if (buffer.readableBytes() < minLength) {
return null; return null;
} }
@ -474,18 +452,9 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
streamId = getUnsignedInt(buffer, offset); streamId = getUnsignedInt(buffer, offset);
int associatedToStreamId = getUnsignedInt(buffer, offset + 4); int associatedToStreamId = getUnsignedInt(buffer, offset + 4);
byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07); byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
if (version < 3) {
priority >>= 1;
}
buffer.skipBytes(10); buffer.skipBytes(10);
length -= 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 = SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority); new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority);
spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0); spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -494,8 +463,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
return spdySynStreamFrame; return spdySynStreamFrame;
case SPDY_SYN_REPLY_FRAME: case SPDY_SYN_REPLY_FRAME:
minLength = version < 3 ? 8 : 4; if (buffer.readableBytes() < 4) {
if (buffer.readableBytes() < minLength) {
return null; return null;
} }
@ -503,18 +471,6 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
buffer.skipBytes(4); buffer.skipBytes(4);
length -= 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 spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0); spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -525,27 +481,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
return null; 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()); streamId = getUnsignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4); buffer.skipBytes(4);
length -= 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 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId);
spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0); spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
@ -562,10 +501,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
return streamId != 0; return streamId != 0;
case SPDY_SYN_STREAM_FRAME: case SPDY_SYN_STREAM_FRAME:
return version < 3 ? length >= 12 : length >= 10; return length >= 10;
case SPDY_SYN_REPLY_FRAME: case SPDY_SYN_REPLY_FRAME:
return version < 3 ? length >= 8 : length >= 4; return length >= 4;
case SPDY_RST_STREAM_FRAME: case SPDY_RST_STREAM_FRAME:
return flags == 0 && length == 8; return flags == 0 && length == 8;
@ -577,14 +516,10 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
return length == 4; return length == 4;
case SPDY_GOAWAY_FRAME: case SPDY_GOAWAY_FRAME:
return version < 3 ? length == 4 : length == 8; return length == 8;
case SPDY_HEADERS_FRAME: case SPDY_HEADERS_FRAME:
if (version < 3) { return length >= 4;
return length == 4 || length >= 8;
} else {
return length >= 4;
}
case SPDY_WINDOW_UPDATE_FRAME: case SPDY_WINDOW_UPDATE_FRAME:
return length == 8; return length == 8;

View File

@ -92,12 +92,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
flags |= SPDY_FLAG_UNIDIRECTIONAL; flags |= SPDY_FLAG_UNIDIRECTIONAL;
} }
int headerBlockLength = data.readableBytes(); int headerBlockLength = data.readableBytes();
int length; int length = 10 + headerBlockLength;
if (version < 3) {
length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
} else {
length = 10 + headerBlockLength;
}
out.ensureWritable(SPDY_HEADER_SIZE + length); out.ensureWritable(SPDY_HEADER_SIZE + length);
out.writeShort(version | 0x8000); out.writeShort(version | 0x8000);
out.writeShort(SPDY_SYN_STREAM_FRAME); out.writeShort(SPDY_SYN_STREAM_FRAME);
@ -105,19 +100,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
out.writeMedium(length); out.writeMedium(length);
out.writeInt(spdySynStreamFrame.getStreamId()); out.writeInt(spdySynStreamFrame.getStreamId());
out.writeInt(spdySynStreamFrame.getAssociatedToStreamId()); out.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
if (version < 3) { out.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
// 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.writeBytes(data, data.readerIndex(), headerBlockLength); out.writeBytes(data, data.readerIndex(), headerBlockLength);
} finally { } finally {
data.release(); data.release();
@ -130,25 +113,13 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
try { try {
byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0; byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
int headerBlockLength = data.readableBytes(); int headerBlockLength = data.readableBytes();
int length; int length = 4 + headerBlockLength;
if (version < 3) {
length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
} else {
length = 4 + headerBlockLength;
}
out.ensureWritable(SPDY_HEADER_SIZE + length); out.ensureWritable(SPDY_HEADER_SIZE + length);
out.writeShort(version | 0x8000); out.writeShort(version | 0x8000);
out.writeShort(SPDY_SYN_REPLY_FRAME); out.writeShort(SPDY_SYN_REPLY_FRAME);
out.writeByte(flags); out.writeByte(flags);
out.writeMedium(length); out.writeMedium(length);
out.writeInt(spdySynReplyFrame.getStreamId()); out.writeInt(spdySynReplyFrame.getStreamId());
if (version < 3) {
if (headerBlockLength == 0) {
out.writeInt(0);
} else {
out.writeShort(0);
}
}
out.writeBytes(data, data.readerIndex(), headerBlockLength); out.writeBytes(data, data.readerIndex(), headerBlockLength);
} finally { } finally {
data.release(); data.release();
@ -178,8 +149,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
out.writeByte(flags); out.writeByte(flags);
out.writeMedium(length); out.writeMedium(length);
out.writeInt(numEntries); out.writeInt(numEntries);
for (Integer ID: IDs) { for (Integer id: IDs) {
int id = ID.intValue();
byte ID_flags = 0; byte ID_flags = 0;
if (spdySettingsFrame.isPersistValue(id)) { if (spdySettingsFrame.isPersistValue(id)) {
ID_flags |= SPDY_SETTINGS_PERSIST_VALUE; ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
@ -187,18 +157,8 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
if (spdySettingsFrame.isPersisted(id)) { if (spdySettingsFrame.isPersisted(id)) {
ID_flags |= SPDY_SETTINGS_PERSISTED; ID_flags |= SPDY_SETTINGS_PERSISTED;
} }
if (version < 3) { out.writeByte(ID_flags);
// Chromium Issue 79156 out.writeMedium(id);
// 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.writeInt(spdySettingsFrame.getValue(id)); out.writeInt(spdySettingsFrame.getValue(id));
} }
@ -214,15 +174,12 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
} else if (msg instanceof SpdyGoAwayFrame) { } else if (msg instanceof SpdyGoAwayFrame) {
SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg; SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
int length = version < 3 ? 4 : 8; out.ensureWritable(SPDY_HEADER_SIZE + 8);
out.ensureWritable(SPDY_HEADER_SIZE + length);
out.writeShort(version | 0x8000); out.writeShort(version | 0x8000);
out.writeShort(SPDY_GOAWAY_FRAME); out.writeShort(SPDY_GOAWAY_FRAME);
out.writeInt(length); out.writeInt(8);
out.writeInt(spdyGoAwayFrame.getLastGoodStreamId()); out.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
if (version >= 3) { out.writeInt(spdyGoAwayFrame.getStatus().getCode());
out.writeInt(spdyGoAwayFrame.getStatus().getCode());
}
} else if (msg instanceof SpdyHeadersFrame) { } else if (msg instanceof SpdyHeadersFrame) {
@ -231,21 +188,13 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<SpdyFrame> {
try { try {
byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0; byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
int headerBlockLength = data.readableBytes(); int headerBlockLength = data.readableBytes();
int length; int length = 4 + headerBlockLength;
if (version < 3) {
length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
} else {
length = 4 + headerBlockLength;
}
out.ensureWritable(SPDY_HEADER_SIZE + length); out.ensureWritable(SPDY_HEADER_SIZE + length);
out.writeShort(version | 0x8000); out.writeShort(version | 0x8000);
out.writeShort(SPDY_HEADERS_FRAME); out.writeShort(SPDY_HEADERS_FRAME);
out.writeByte(flags); out.writeByte(flags);
out.writeMedium(length); out.writeMedium(length);
out.writeInt(spdyHeadersFrame.getStreamId()); out.writeInt(spdyHeadersFrame.getStreamId());
if (version < 3 && headerBlockLength != 0) {
out.writeShort(0);
}
out.writeBytes(data, data.readerIndex(), headerBlockLength); out.writeBytes(data, data.readerIndex(), headerBlockLength);
} finally { } finally {
data.release(); data.release();

View File

@ -39,7 +39,6 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
// TODO: Replace with generic NPN handler // TODO: Replace with generic NPN handler
public enum SelectedProtocol { public enum SelectedProtocol {
SPDY_2,
SPDY_3, SPDY_3,
SPDY_3_1, SPDY_3_1,
HTTP_1_1, HTTP_1_1,
@ -84,9 +83,6 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
case UNKNOWN: case UNKNOWN:
// Not done with choosing the protocol, so just return here for now, // Not done with choosing the protocol, so just return here for now,
return false; return false;
case SPDY_2:
addSpdyHandlers(ctx, SpdyVersion.SPDY_2);
break;
case SPDY_3: case SPDY_3:
addSpdyHandlers(ctx, SpdyVersion.SPDY_3); addSpdyHandlers(ctx, SpdyVersion.SPDY_3);
break; break;
@ -137,8 +133,8 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder {
/** /**
* Create the {@link ChannelInboundHandler} that is responsible for handling the http responses * Create the {@link ChannelInboundHandler} that is responsible for handling the http responses
* when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_2} or * when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_3} or
* {@link SelectedProtocol#SPDY_3}. * {@link SelectedProtocol#SPDY_3_1}.
* *
* By default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but * By default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
* sub-classes may override this to change the behaviour. * sub-classes may override this to change the behaviour.

View File

@ -61,7 +61,6 @@ public class SpdySessionHandler
private ChannelFutureListener closeSessionFutureListener; private ChannelFutureListener closeSessionFutureListener;
private final boolean server; private final boolean server;
private final boolean flowControl;
private final boolean sessionFlowControl; private final boolean sessionFlowControl;
/** /**
@ -78,7 +77,6 @@ public class SpdySessionHandler
throw new NullPointerException("version"); throw new NullPointerException("version");
} }
this.server = server; this.server = server;
flowControl = version.useFlowControl();
sessionFlowControl = version.useSessionFlowControl(); 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. * Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
*/ */
if (flowControl) { // Update receive window size
// Update receive window size int deltaWindowSize = -1 * spdyDataFrame.content().readableBytes();
int deltaWindowSize = -1 * spdyDataFrame.content().readableBytes(); int newWindowSize = spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
int newWindowSize = spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
// Window size can become negative if we sent a SETTINGS frame that reduces the // 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. // 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. // 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 // This difference is stored for the session when writing the SETTINGS frame
// and is cleared once we send a WINDOW_UPDATE frame. // and is cleared once we send a WINDOW_UPDATE frame.
if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) { if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) {
spdyDataFrame.release(); spdyDataFrame.release();
issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR); issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
return; return;
} }
// Window size became negative due to sender writing frame before receiving SETTINGS // Window size became negative due to sender writing frame before receiving SETTINGS
// Send data frames upstream in initialReceiveWindowSize chunks // Send data frames upstream in initialReceiveWindowSize chunks
if (newWindowSize < 0) { if (newWindowSize < 0) {
while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) { while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) {
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId, SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId,
spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain()); spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain());
ctx.writeAndFlush(partialDataFrame); ctx.writeAndFlush(partialDataFrame);
}
} }
}
// Send a WINDOW_UPDATE frame if less than half the stream window size remains // Send a WINDOW_UPDATE frame if less than half the stream window size remains
if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) { if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) {
deltaWindowSize = initialReceiveWindowSize - newWindowSize; deltaWindowSize = initialReceiveWindowSize - newWindowSize;
spdySession.updateReceiveWindowSize(streamId, deltaWindowSize); spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);
SpdyWindowUpdateFrame spdyWindowUpdateFrame = SpdyWindowUpdateFrame spdyWindowUpdateFrame =
new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize); new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
ctx.writeAndFlush(spdyWindowUpdateFrame); ctx.writeAndFlush(spdyWindowUpdateFrame);
}
} }
// Close the remote side of the stream if this is the last frame // 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); spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
if (flowControl) { int newInitialWindowSize =
int newInitialWindowSize = spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE); if (newInitialWindowSize >= 0) {
if (newInitialWindowSize >= 0) { updateInitialSendWindowSize(newInitialWindowSize);
updateInitialSendWindowSize(newInitialWindowSize);
}
} }
} else if (msg instanceof SpdyPingFrame) { } else if (msg instanceof SpdyPingFrame) {
@ -382,28 +376,26 @@ public class SpdySessionHandler
* after sending the last frame for the stream. * after sending the last frame for the stream.
*/ */
if (flowControl) { SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg; int streamId = spdyWindowUpdateFrame.getStreamId();
int streamId = spdyWindowUpdateFrame.getStreamId(); int deltaWindowSize = spdyWindowUpdateFrame.getDeltaWindowSize();
int deltaWindowSize = spdyWindowUpdateFrame.getDeltaWindowSize();
// Ignore frames for half-closed streams // Ignore frames for half-closed streams
if (streamId != SPDY_SESSION_STREAM_ID && spdySession.isLocalSideClosed(streamId)) { if (streamId != SPDY_SESSION_STREAM_ID && spdySession.isLocalSideClosed(streamId)) {
return; 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);
} }
// 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); ctx.fireChannelRead(msg);
@ -475,65 +467,63 @@ public class SpdySessionHandler
* sender must pause transmitting data frames. * sender must pause transmitting data frames.
*/ */
if (flowControl) { synchronized (flowControlLock) {
synchronized (flowControlLock) { int dataLength = spdyDataFrame.content().readableBytes();
int dataLength = spdyDataFrame.content().readableBytes(); int sendWindowSize = spdySession.getSendWindowSize(streamId);
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) { if (sessionFlowControl) {
int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID); spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);
sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);
} }
if (sendWindowSize <= 0) { // Create a partial data frame whose length is the current window size
// Stream is stalled -- enqueue Data frame and return SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId,
spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise)); spdyDataFrame.content().readSlice(sendWindowSize).retain());
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 // Enqueue the remaining data (will be the first frame queued)
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId, spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));
spdyDataFrame.content().readSlice(sendWindowSize).retain());
// Enqueue the remaining data (will be the first frame queued) // The transfer window size is pre-decremented when sending a data frame downstream.
spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise)); // Close the session on write failures that leave the transfer window in a corrupt state.
final ChannelHandlerContext context = ctx;
// The transfer window size is pre-decremented when sending a data frame downstream. ctx.write(partialDataFrame).addListener(new ChannelFutureListener() {
// Close the session on write failures that leave the transfer window in a corrupt state. @Override
final ChannelHandlerContext context = ctx; public void operationComplete(ChannelFuture future) throws Exception {
ctx.write(partialDataFrame).addListener(new ChannelFutureListener() { if (!future.isSuccess()) {
@Override issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
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. return;
// Close the session on write failures that leave the transfer window in a corrupt state. } else {
final ChannelHandlerContext context = ctx; // Window size is large enough to send entire data frame
promise.addListener(new ChannelFutureListener() { spdySession.updateSendWindowSize(streamId, -1 * dataLength);
@Override if (sessionFlowControl) {
public void operationComplete(ChannelFuture future) throws Exception { spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);
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;
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); spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
if (flowControl) { int newInitialWindowSize =
int newInitialWindowSize = spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE); if (newInitialWindowSize >= 0) {
if (newInitialWindowSize >= 0) { updateInitialReceiveWindowSize(newInitialWindowSize);
updateInitialReceiveWindowSize(newInitialWindowSize);
}
} }
} else if (msg instanceof SpdyPingFrame) { } else if (msg instanceof SpdyPingFrame) {

View File

@ -16,28 +16,14 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
public enum SpdyVersion { public enum SpdyVersion {
SPDY_2 (2, false, false), SPDY_3 (3, false),
SPDY_3 (3, true, false), SPDY_3_1 (3, true);
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);
}
private final int version; private final int version;
private final boolean flowControl;
private final boolean sessionFlowControl; private final boolean sessionFlowControl;
private SpdyVersion(int version, boolean flowControl, boolean sessionFlowControl) { private SpdyVersion(int version, boolean sessionFlowControl) {
this.version = version; this.version = version;
this.flowControl = flowControl;
this.sessionFlowControl = sessionFlowControl; this.sessionFlowControl = sessionFlowControl;
} }
@ -45,10 +31,6 @@ public enum SpdyVersion {
return version; return version;
} }
boolean useFlowControl() {
return flowControl;
}
boolean useSessionFlowControl() { boolean useSessionFlowControl() {
return sessionFlowControl; return sessionFlowControl;
} }

View File

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

View File

@ -282,8 +282,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyClientSessionHandler() { public void testSpdyClientSessionHandler() {
logger.info("Running: testSpdyClientSessionHandler v2");
testSpdySessionHandler(SpdyVersion.SPDY_2, false);
logger.info("Running: testSpdyClientSessionHandler v3"); logger.info("Running: testSpdyClientSessionHandler v3");
testSpdySessionHandler(SpdyVersion.SPDY_3, false); testSpdySessionHandler(SpdyVersion.SPDY_3, false);
logger.info("Running: testSpdyClientSessionHandler v3.1"); logger.info("Running: testSpdyClientSessionHandler v3.1");
@ -292,8 +290,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyClientSessionHandlerPing() { public void testSpdyClientSessionHandlerPing() {
logger.info("Running: testSpdyClientSessionHandlerPing v2");
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, false);
logger.info("Running: testSpdyClientSessionHandlerPing v3"); logger.info("Running: testSpdyClientSessionHandlerPing v3");
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, false); testSpdySessionHandlerPing(SpdyVersion.SPDY_3, false);
logger.info("Running: testSpdyClientSessionHandlerPing v3.1"); logger.info("Running: testSpdyClientSessionHandlerPing v3.1");
@ -302,8 +298,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyClientSessionHandlerGoAway() { public void testSpdyClientSessionHandlerGoAway() {
logger.info("Running: testSpdyClientSessionHandlerGoAway v2");
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, false);
logger.info("Running: testSpdyClientSessionHandlerGoAway v3"); logger.info("Running: testSpdyClientSessionHandlerGoAway v3");
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, false); testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, false);
logger.info("Running: testSpdyClientSessionHandlerGoAway v3.1"); logger.info("Running: testSpdyClientSessionHandlerGoAway v3.1");
@ -312,8 +306,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyServerSessionHandler() { public void testSpdyServerSessionHandler() {
logger.info("Running: testSpdyServerSessionHandler v2");
testSpdySessionHandler(SpdyVersion.SPDY_2, true);
logger.info("Running: testSpdyServerSessionHandler v3"); logger.info("Running: testSpdyServerSessionHandler v3");
testSpdySessionHandler(SpdyVersion.SPDY_3, true); testSpdySessionHandler(SpdyVersion.SPDY_3, true);
logger.info("Running: testSpdyServerSessionHandler v3.1"); logger.info("Running: testSpdyServerSessionHandler v3.1");
@ -322,8 +314,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyServerSessionHandlerPing() { public void testSpdyServerSessionHandlerPing() {
logger.info("Running: testSpdyServerSessionHandlerPing v2");
testSpdySessionHandlerPing(SpdyVersion.SPDY_2, true);
logger.info("Running: testSpdyServerSessionHandlerPing v3"); logger.info("Running: testSpdyServerSessionHandlerPing v3");
testSpdySessionHandlerPing(SpdyVersion.SPDY_3, true); testSpdySessionHandlerPing(SpdyVersion.SPDY_3, true);
logger.info("Running: testSpdyServerSessionHandlerPing v3.1"); logger.info("Running: testSpdyServerSessionHandlerPing v3.1");
@ -332,8 +322,6 @@ public class SpdySessionHandlerTest {
@Test @Test
public void testSpdyServerSessionHandlerGoAway() { public void testSpdyServerSessionHandlerGoAway() {
logger.info("Running: testSpdyServerSessionHandlerGoAway v2");
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_2, true);
logger.info("Running: testSpdyServerSessionHandlerGoAway v3"); logger.info("Running: testSpdyServerSessionHandlerGoAway v3");
testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, true); testSpdySessionHandlerGoAway(SpdyVersion.SPDY_3, true);
logger.info("Running: testSpdyServerSessionHandlerGoAway v3.1"); logger.info("Running: testSpdyServerSessionHandlerGoAway v3.1");

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, * 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 * 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; static final int ignoredBytes = 20;
private static ByteBuf createFrames(int version) { private static ByteBuf createFrames(int version) {
int length = version < 3 ? 1176 : 1174; ByteBuf frames = Unpooled.buffer(1174);
ByteBuf frames = Unpooled.buffer(length);
// SPDY UNKNOWN Frame // SPDY UNKNOWN Frame
frames.writeByte(0x80); frames.writeByte(0x80);
@ -74,11 +73,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
frames.writeByte(version); frames.writeByte(version);
frames.writeShort(1); frames.writeShort(1);
frames.writeByte(0x03); frames.writeByte(0x03);
if (version < 3) { frames.writeMedium(10);
frames.writeMedium(12);
} else {
frames.writeMedium(10);
}
frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01); frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01);
frames.writeInt(random.nextInt() & 0x7FFFFFFF); frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeShort(0x8000); frames.writeShort(0x8000);
@ -91,11 +86,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
frames.writeByte(version); frames.writeByte(version);
frames.writeShort(2); frames.writeShort(2);
frames.writeByte(0x01); frames.writeByte(0x01);
if (version < 3) { frames.writeMedium(4);
frames.writeMedium(8);
} else {
frames.writeMedium(4);
}
frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01); frames.writeInt(random.nextInt() & 0x7FFFFFFF | 0x01);
if (version < 3) { if (version < 3) {
frames.writeInt(0); frames.writeInt(0);
@ -116,13 +107,8 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
frames.writeByte(0x01); frames.writeByte(0x01);
frames.writeMedium(12); frames.writeMedium(12);
frames.writeInt(1); frames.writeInt(1);
if (version < 3) { frames.writeByte(0x03);
frames.writeMedium(random.nextInt()); frames.writeMedium(random.nextInt());
frames.writeByte(0x03);
} else {
frames.writeByte(0x03);
frames.writeMedium(random.nextInt());
}
frames.writeInt(random.nextInt()); frames.writeInt(random.nextInt());
// SPDY PING Frame // SPDY PING Frame
@ -136,15 +122,9 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
frames.writeByte(0x80); frames.writeByte(0x80);
frames.writeByte(version); frames.writeByte(version);
frames.writeShort(7); frames.writeShort(7);
if (version < 3) { frames.writeInt(8);
frames.writeInt(4);
} else {
frames.writeInt(8);
}
frames.writeInt(random.nextInt() & 0x7FFFFFFF); frames.writeInt(random.nextInt() & 0x7FFFFFFF);
if (version >= 3) { frames.writeInt(random.nextInt() | 0x01);
frames.writeInt(random.nextInt() | 0x01);
}
// SPDY HEADERS Frame // SPDY HEADERS Frame
frames.writeByte(0x80); frames.writeByte(0x80);
@ -169,10 +149,6 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
@Test(timeout = 15000) @Test(timeout = 15000)
public void testSpdyEcho() throws Throwable { public void testSpdyEcho() throws Throwable {
version = SpdyVersion.SPDY_2;
logger.info("Testing against SPDY v2");
run();
version = SpdyVersion.SPDY_3; version = SpdyVersion.SPDY_3;
logger.info("Testing against SPDY v3"); logger.info("Testing against SPDY v3");
run(); run();
@ -186,9 +162,6 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
ByteBuf frames; ByteBuf frames;
switch (version) { switch (version) {
case SPDY_2:
frames = createFrames(2);
break;
case SPDY_3: case SPDY_3:
case SPDY_3_1: case SPDY_3_1:
frames = createFrames(3); frames = createFrames(3);