2012-05-31 09:37:27 +02:00
|
|
|
/*
|
2014-04-13 22:55:29 +02:00
|
|
|
* Copyright 2014 The Netty Project
|
2012-05-31 09:37:27 +02:00
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
2012-06-04 22:31:44 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2012-05-31 09:37:27 +02:00
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
|
|
|
package io.netty.handler.codec.spdy;
|
|
|
|
|
Remove MessageList from public API and change ChannelInbound/OutboundHandler accordingly
I must admit MesageList was pain in the ass. Instead of forcing a
handler always loop over the list of messages, this commit splits
messageReceived(ctx, list) into two event handlers:
- messageReceived(ctx, msg)
- mmessageReceivedLast(ctx)
When Netty reads one or more messages, messageReceived(ctx, msg) event
is triggered for each message. Once the current read operation is
finished, messageReceivedLast() is triggered to tell the handler that
the last messageReceived() was the last message in the current batch.
Similarly, for outbound, write(ctx, list) has been split into two:
- write(ctx, msg)
- flush(ctx, promise)
Instead of writing a list of message with a promise, a user is now
supposed to call write(msg) multiple times and then call flush() to
actually flush the buffered messages.
Please note that write() doesn't have a promise with it. You must call
flush() to get notified on completion. (or you can use writeAndFlush())
Other changes:
- Because MessageList is completely hidden, codec framework uses
List<Object> instead of MessageList as an output parameter.
2013-07-08 12:03:40 +02:00
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_DATA_FLAG_FIN;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_DATA_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_FLAG_FIN;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_FLAG_UNIDIRECTIONAL;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_GOAWAY_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADERS_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_FLAGS_OFFSET;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_LENGTH_OFFSET;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_SIZE;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_TYPE_OFFSET;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_PING_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_RST_STREAM_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_CLEAR;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_PERSISTED;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_PERSIST_VALUE;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SYN_REPLY_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SYN_STREAM_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_WINDOW_UPDATE_FRAME;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.getSignedInt;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedInt;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedMedium;
|
|
|
|
import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedShort;
|
2019-02-04 15:55:07 +01:00
|
|
|
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
|
|
|
|
2012-06-10 04:08:43 +02:00
|
|
|
import io.netty.buffer.ByteBuf;
|
2014-04-13 22:55:29 +02:00
|
|
|
import io.netty.buffer.Unpooled;
|
2013-01-14 14:59:11 +01:00
|
|
|
|
2012-05-31 09:37:27 +02:00
|
|
|
/**
|
2013-06-04 23:32:11 +02:00
|
|
|
* Decodes {@link ByteBuf}s into SPDY Frames.
|
2012-05-31 09:37:27 +02:00
|
|
|
*/
|
2014-04-13 22:55:29 +02:00
|
|
|
public class SpdyFrameDecoder {
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2012-05-31 09:37:27 +02:00
|
|
|
private final int spdyVersion;
|
|
|
|
private final int maxChunkSize;
|
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
private final SpdyFrameDecoderDelegate delegate;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
|
|
|
private State state;
|
|
|
|
|
|
|
|
// SPDY common header fields
|
|
|
|
private byte flags;
|
|
|
|
private int length;
|
2013-06-17 08:01:56 +02:00
|
|
|
private int streamId;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
private int numSettings;
|
|
|
|
|
2012-06-11 15:54:28 +02:00
|
|
|
private enum State {
|
2012-05-31 09:37:27 +02:00
|
|
|
READ_COMMON_HEADER,
|
2014-04-13 22:55:29 +02:00
|
|
|
READ_DATA_FRAME,
|
|
|
|
READ_SYN_STREAM_FRAME,
|
|
|
|
READ_SYN_REPLY_FRAME,
|
|
|
|
READ_RST_STREAM_FRAME,
|
2012-05-31 09:37:27 +02:00
|
|
|
READ_SETTINGS_FRAME,
|
2014-04-13 22:55:29 +02:00
|
|
|
READ_SETTING,
|
|
|
|
READ_PING_FRAME,
|
|
|
|
READ_GOAWAY_FRAME,
|
|
|
|
READ_HEADERS_FRAME,
|
|
|
|
READ_WINDOW_UPDATE_FRAME,
|
2012-05-31 09:37:27 +02:00
|
|
|
READ_HEADER_BLOCK,
|
|
|
|
DISCARD_FRAME,
|
|
|
|
FRAME_ERROR
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-04-13 22:55:29 +02:00
|
|
|
* Creates a new instance with the specified {@code version}
|
|
|
|
* and the default {@code maxChunkSize (8192)}.
|
2012-05-31 09:37:27 +02:00
|
|
|
*/
|
2014-04-13 22:55:29 +02:00
|
|
|
public SpdyFrameDecoder(SpdyVersion spdyVersion, SpdyFrameDecoderDelegate delegate) {
|
|
|
|
this(spdyVersion, delegate, 8192);
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new instance with the specified parameters.
|
|
|
|
*/
|
2014-04-13 22:55:29 +02:00
|
|
|
public SpdyFrameDecoder(SpdyVersion spdyVersion, SpdyFrameDecoderDelegate delegate, int maxChunkSize) {
|
|
|
|
if (spdyVersion == null) {
|
|
|
|
throw new NullPointerException("spdyVersion");
|
|
|
|
}
|
|
|
|
if (delegate == null) {
|
|
|
|
throw new NullPointerException("delegate");
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
2019-02-04 15:55:07 +01:00
|
|
|
checkPositive(maxChunkSize, "maxChunkSize");
|
2014-04-13 22:55:29 +02:00
|
|
|
this.spdyVersion = spdyVersion.getVersion();
|
|
|
|
this.delegate = delegate;
|
2012-05-31 09:37:27 +02:00
|
|
|
this.maxChunkSize = maxChunkSize;
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
}
|
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
public void decode(ByteBuf buffer) {
|
|
|
|
boolean last;
|
|
|
|
int statusCode;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
while (true) {
|
|
|
|
switch(state) {
|
|
|
|
case READ_COMMON_HEADER:
|
|
|
|
if (buffer.readableBytes() < SPDY_HEADER_SIZE) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
int frameOffset = buffer.readerIndex();
|
|
|
|
int flagsOffset = frameOffset + SPDY_HEADER_FLAGS_OFFSET;
|
|
|
|
int lengthOffset = frameOffset + SPDY_HEADER_LENGTH_OFFSET;
|
|
|
|
buffer.skipBytes(SPDY_HEADER_SIZE);
|
|
|
|
|
|
|
|
boolean control = (buffer.getByte(frameOffset) & 0x80) != 0;
|
|
|
|
|
|
|
|
int version;
|
|
|
|
int type;
|
|
|
|
if (control) {
|
|
|
|
// Decode control frame common header
|
|
|
|
version = getUnsignedShort(buffer, frameOffset) & 0x7FFF;
|
|
|
|
type = getUnsignedShort(buffer, frameOffset + SPDY_HEADER_TYPE_OFFSET);
|
|
|
|
streamId = 0; // Default to session Stream-ID
|
|
|
|
} else {
|
|
|
|
// Decode data frame common header
|
|
|
|
version = spdyVersion; // Default to expected version
|
|
|
|
type = SPDY_DATA_FRAME;
|
|
|
|
streamId = getUnsignedInt(buffer, frameOffset);
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
flags = buffer.getByte(flagsOffset);
|
|
|
|
length = getUnsignedMedium(buffer, lengthOffset);
|
|
|
|
|
|
|
|
// Check version first then validity
|
|
|
|
if (version != spdyVersion) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid SPDY Version");
|
|
|
|
} else if (!isValidFrameHeader(streamId, type, flags, length)) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid Frame Error");
|
|
|
|
} else {
|
|
|
|
state = getNextState(type, length);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_DATA_FRAME:
|
|
|
|
if (length == 0) {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readDataFrame(streamId, hasFlag(flags, SPDY_DATA_FLAG_FIN), Unpooled.buffer(0));
|
|
|
|
break;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
// Generate data frames that do not exceed maxChunkSize
|
|
|
|
int dataLength = Math.min(maxChunkSize, length);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
// Wait until entire frame is readable
|
|
|
|
if (buffer.readableBytes() < dataLength) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
ByteBuf data = buffer.alloc().buffer(dataLength);
|
|
|
|
data.writeBytes(buffer, dataLength);
|
|
|
|
length -= dataLength;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
|
|
|
if (length == 0) {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
2014-04-13 22:55:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
last = length == 0 && hasFlag(flags, SPDY_DATA_FLAG_FIN);
|
|
|
|
|
|
|
|
delegate.readDataFrame(streamId, last, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case READ_SYN_STREAM_FRAME:
|
|
|
|
if (buffer.readableBytes() < 10) {
|
2013-04-03 11:32:33 +02:00
|
|
|
return;
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
int offset = buffer.readerIndex();
|
|
|
|
streamId = getUnsignedInt(buffer, offset);
|
|
|
|
int associatedToStreamId = getUnsignedInt(buffer, offset + 4);
|
|
|
|
byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
|
|
|
|
last = hasFlag(flags, SPDY_FLAG_FIN);
|
|
|
|
boolean unidirectional = hasFlag(flags, SPDY_FLAG_UNIDIRECTIONAL);
|
|
|
|
buffer.skipBytes(10);
|
|
|
|
length -= 10;
|
|
|
|
|
|
|
|
if (streamId == 0) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid SYN_STREAM Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_HEADER_BLOCK;
|
|
|
|
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, last, unidirectional);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_SYN_REPLY_FRAME:
|
|
|
|
if (buffer.readableBytes() < 4) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-28 09:40:01 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
streamId = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
last = hasFlag(flags, SPDY_FLAG_FIN);
|
2013-06-21 01:37:00 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
buffer.skipBytes(4);
|
|
|
|
length -= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (streamId == 0) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid SYN_REPLY Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_HEADER_BLOCK;
|
|
|
|
delegate.readSynReplyFrame(streamId, last);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_RST_STREAM_FRAME:
|
|
|
|
if (buffer.readableBytes() < 8) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
streamId = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
|
|
|
|
buffer.skipBytes(8);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (streamId == 0 || statusCode == 0) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid RST_STREAM Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readRstStreamFrame(streamId, statusCode);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_SETTINGS_FRAME:
|
|
|
|
if (buffer.readableBytes() < 4) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
boolean clear = hasFlag(flags, SPDY_SETTINGS_CLEAR);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
numSettings = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
buffer.skipBytes(4);
|
|
|
|
length -= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
// Validate frame length against number of entries. Each ID/Value entry is 8 bytes.
|
|
|
|
if ((length & 0x07) != 0 || length >> 3 != numSettings) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid SETTINGS Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_SETTING;
|
|
|
|
delegate.readSettingsFrame(clear);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_SETTING:
|
|
|
|
if (numSettings == 0) {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readSettingsEnd();
|
|
|
|
break;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (buffer.readableBytes() < 8) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
byte settingsFlags = buffer.getByte(buffer.readerIndex());
|
|
|
|
int id = getUnsignedMedium(buffer, buffer.readerIndex() + 1);
|
|
|
|
int value = getSignedInt(buffer, buffer.readerIndex() + 4);
|
|
|
|
boolean persistValue = hasFlag(settingsFlags, SPDY_SETTINGS_PERSIST_VALUE);
|
|
|
|
boolean persisted = hasFlag(settingsFlags, SPDY_SETTINGS_PERSISTED);
|
|
|
|
buffer.skipBytes(8);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
--numSettings;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
delegate.readSetting(id, value, persistValue, persisted);
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_PING_FRAME:
|
|
|
|
if (buffer.readableBytes() < 4) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
int pingId = getSignedInt(buffer, buffer.readerIndex());
|
|
|
|
buffer.skipBytes(4);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readPingFrame(pingId);
|
|
|
|
break;
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_GOAWAY_FRAME:
|
|
|
|
if (buffer.readableBytes() < 8) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
int lastGoodStreamId = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
|
|
|
|
buffer.skipBytes(8);
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
|
|
|
|
break;
|
2013-06-17 08:01:56 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_HEADERS_FRAME:
|
|
|
|
if (buffer.readableBytes() < 4) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
streamId = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
last = hasFlag(flags, SPDY_FLAG_FIN);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
buffer.skipBytes(4);
|
|
|
|
length -= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (streamId == 0) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid HEADERS Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_HEADER_BLOCK;
|
|
|
|
delegate.readHeadersFrame(streamId, last);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_WINDOW_UPDATE_FRAME:
|
|
|
|
if (buffer.readableBytes() < 8) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
streamId = getUnsignedInt(buffer, buffer.readerIndex());
|
|
|
|
int deltaWindowSize = getUnsignedInt(buffer, buffer.readerIndex() + 4);
|
|
|
|
buffer.skipBytes(8);
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (deltaWindowSize == 0) {
|
|
|
|
state = State.FRAME_ERROR;
|
|
|
|
delegate.readFrameError("Invalid WINDOW_UPDATE Frame");
|
|
|
|
} else {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case READ_HEADER_BLOCK:
|
|
|
|
if (length == 0) {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
delegate.readHeaderBlockEnd();
|
|
|
|
break;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
if (!buffer.isReadable()) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
int compressedBytes = Math.min(buffer.readableBytes(), length);
|
|
|
|
ByteBuf headerBlock = buffer.alloc().buffer(compressedBytes);
|
|
|
|
headerBlock.writeBytes(buffer, compressedBytes);
|
|
|
|
length -= compressedBytes;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
delegate.readHeaderBlock(headerBlock);
|
|
|
|
break;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case DISCARD_FRAME:
|
|
|
|
int numBytes = Math.min(buffer.readableBytes(), length);
|
|
|
|
buffer.skipBytes(numBytes);
|
|
|
|
length -= numBytes;
|
|
|
|
if (length == 0) {
|
|
|
|
state = State.READ_COMMON_HEADER;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case FRAME_ERROR:
|
|
|
|
buffer.skipBytes(buffer.readableBytes());
|
|
|
|
return;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
default:
|
|
|
|
throw new Error("Shouldn't reach here.");
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
private static boolean hasFlag(byte flags, byte flag) {
|
|
|
|
return (flags & flag) != 0;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
private static State getNextState(int type, int length) {
|
|
|
|
switch (type) {
|
|
|
|
case SPDY_DATA_FRAME:
|
|
|
|
return State.READ_DATA_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SYN_STREAM_FRAME:
|
|
|
|
return State.READ_SYN_STREAM_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SYN_REPLY_FRAME:
|
|
|
|
return State.READ_SYN_REPLY_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_RST_STREAM_FRAME:
|
|
|
|
return State.READ_RST_STREAM_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SETTINGS_FRAME:
|
|
|
|
return State.READ_SETTINGS_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_PING_FRAME:
|
|
|
|
return State.READ_PING_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_GOAWAY_FRAME:
|
|
|
|
return State.READ_GOAWAY_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_HEADERS_FRAME:
|
|
|
|
return State.READ_HEADERS_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_WINDOW_UPDATE_FRAME:
|
|
|
|
return State.READ_WINDOW_UPDATE_FRAME;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
default:
|
|
|
|
if (length != 0) {
|
|
|
|
return State.DISCARD_FRAME;
|
|
|
|
} else {
|
|
|
|
return State.READ_COMMON_HEADER;
|
|
|
|
}
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
private static boolean isValidFrameHeader(int streamId, int type, byte flags, int length) {
|
2012-05-31 09:37:27 +02:00
|
|
|
switch (type) {
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_DATA_FRAME:
|
|
|
|
return streamId != 0;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SYN_STREAM_FRAME:
|
|
|
|
return length >= 10;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SYN_REPLY_FRAME:
|
|
|
|
return length >= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_RST_STREAM_FRAME:
|
|
|
|
return flags == 0 && length == 8;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_SETTINGS_FRAME:
|
|
|
|
return length >= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_PING_FRAME:
|
|
|
|
return length == 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_GOAWAY_FRAME:
|
|
|
|
return length == 8;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_HEADERS_FRAME:
|
|
|
|
return length >= 4;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
case SPDY_WINDOW_UPDATE_FRAME:
|
|
|
|
return length == 8;
|
2012-05-31 09:37:27 +02:00
|
|
|
|
2014-04-13 22:55:29 +02:00
|
|
|
default:
|
|
|
|
return true;
|
2012-05-31 09:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|